0

非同期ディスパッチを使用しているときに、非常に奇妙なアクセス エラーが発生します。私は自分のプログラムでこのコード セグメントにまで削減することができました。

-(void)buttonTapped:(id)sender {

    __block NSArray*foo = nil;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        //Foo was initially declared here but then moved it outside.
        foo = [self someMethod];
        [foo retain]; // bad access here. Why ?
        dispatch_async(dispatch_get_main_queue(),0) {
            // doesnt matter what happens here
    }); });
}

-(id)someMethod 
{
    return [self secondMethod];
}

-(id)secondMethod
{
    // was initially returning an autoreleased object from here. Changed it
    // to eliminate that as source of the error.
    id newThing = [[NSObject alloc] init];
    return newThing;
}

コードは最初はこのようには見えませんでしたが、現在はこのようになっています。ダミーの NSObject の割り当てを含みます。

foo がディスパッチ async 内の呼び出しの間に解放される可能性はありますか? これがどのように可能かわかりません。これだけで何が起こっているのかを示唆するのは難しいことを知っていますが、デバッグの提案は役に立ちます。NSZombies をオンにしてみましたが、ゾンビが表示されません。

4

1 に答える 1

1

あなたが尋ねる:

foo内の呼び出しの間に解放されるのはどうすれば可能dispatch_asyncですか?

一時的に自動解放プールを空にする可能性のある何かを非同期的に実行しない限りsomeMethod、または実行中である場合を除き、そうすべきではありません。secondMethod

電源を入れてみましNSZombiesたが、ゾンビが表示されません。

ゾンビをオンにしていてゾンビが発生しない場合、問題は別の場所にあるのではないかと思います。率直に言って、質問の目的でサンプルコードを単純化するプロセスで、問題の根本が解消されたと思います。

その他のいくつかの観察/説明:

  1. あなたは であると宣言fooしましたNSArrayが、あなたは を返していますNSObject。私はあなたがそれがずっとあることを意味したと仮定しますNSObject.

  2. 次のようなコード行があります。

    dispatch_async(dispatch_get_main_queue(),0) {
    

    それはタイプミスであり、あなたが意図したものであると仮定します:

    dispatch_async(dispatch_get_main_queue(), ^{
    
  3. foo変数は必ずdispatch_asyncブロック内にある必要があります。何かに対して変数を使用することは、実際には意味がありません__block(a) ブロックのそのブロックの外で参照しない。(b) 非同期にディスパッチするブロックの場合。

  4. どうやら元々持っていたように、オブジェクトをsecondMethod返す必要があります。autorelease(または、混乱を避け、最終的に ARC に移行するときに自分の生活を楽にするために、名前を変更secondMethodsomeMethodて開始することをお勧めします。)new

  5. オブジェクトの場合retainfoo、適切な も追加する必要がありますrelease。実際、元のコード サンプルは +1 オブジェクトを返し、それを再び保持して +2 に上げるので、2 回のrelease呼び出しが必要になります。

とにかく、これらのさまざまな問題を修正すると、次のようになり、例外は生成されません。

- (IBAction)buttonTapped:(id)sender
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

        NSObject *foo = [self someMethod];
        [foo retain]; // no bad access here

        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"foo = %@", foo);

            [foo release];
        });
    });
}

- (NSObject *)someMethod
{
    return [self secondMethod];
}

- (NSObject *)secondMethod
{
    return [[[NSObject alloc] init] autorelease];
}

さらに、特に手動の保持と解放 (MRR) を使用する場合は、静的アナライザー (Xcode の [製品] メニューの [分析]) を使用して実行し、クリーンな状態であることを確認することをお勧めします。(私が言及した問題のいくつかを指摘していたはずです。) 完璧ではありませんが、問題を特定するのは非常に優れています。

しかし、要するに、上記のコードは問題ありません。それでも例外が発生する場合は、例外を再現する作業コードで質問を更新してください。

于 2013-11-06T18:33:10.150 に答える