11

NSOperationコードをARCに変換するのが難しいです。私の操作オブジェクトは、補完ブロックを使用します。完了ブロックには、メインスレッドのUIを更新するGCDブロックが含まれています。私自身の補完ブロック内から自分の操作オブジェクトを参照するので、メモリリークを避けるために__weakポインタを使用しています。しかし、私のコードが実行されるまでにポインタはすでにnilに設定されています。ARCで独自の補完ブロックでNSOperationオブジェクトを参照する

私はこのコードサンプルに絞り込みました。誰が私が間違っていたか、そしてこれを達成する正しい方法を知っていますか?

NSOperationSubclass *operation = [[NSOperationSubclass alloc] init]; 
__weak NSOperationSubclass *weakOperation = operation; 

[operation setCompletionBlock:^{ 
    dispatch_async(dispatch_get_main_queue(), ^{ 

     // fails the check 
     NSAssert(weakOperation != nil, @"pointer is nil"); 

     ... 
    }); 
}]; 
+1

弱点は所有権を保持していないということです。変数を保持しているものがない場合(そして存在しない場合)は、パージされます。あなたは 'operation'を使うとリークが起きますか?完了ブロックがリリースされると消えるはずですが、呼び出されるとすぐに消えるはずです。しかし、それは素朴かもしれません。 –

+0

ARCはコンパイル時にそれについて不平を言っていました。それがなければ、私は操作ポインタを直接使用していました(そして私はメモリが漏れているとは思わない)。 –

+1

これで幸運。私は何かをあきらめて何か他のことをする前に数時間苦労したと思う。しかし、それはしばらくしています。 :) –

答えて

10

私はこれについて確実ではないんだけど、それを行うための正しい方法は、それがあることを保証するために、ブロックの最後にnilに問題の変数に__blockを追加し、それを設定する可能性がありますリリースされました。 See this question.

あなたの新しいコードは次のようになります。

NSOperationSubclass *operation = [[NSOperationSubclass alloc] init]; 
__block NSOperationSubclass *weakOperation = operation; 

[operation setCompletionBlock:^{ 
    dispatch_async(dispatch_get_main_queue(), ^{ 

     // fails the check 
     NSAssert(weakOperation != nil, @"pointer is nil"); 

     ... 
     weakOperation = nil; 
    }); 

}]; 
+3

あなたは正しいと私は信じています。ありがとう! –

14

別のオプションは、次のようになります。

NSOperationSubclass *operation = [[NSOperationSubclass alloc] init]; 
__weak NSOperationSubclass *weakOperation = operation; 

[operation setCompletionBlock:^{ 
    NSOperationSubclass *strongOperation = weakOperation; 

    dispatch_async(dispatch_get_main_queue(), ^{ 
     assert(strongOperation != nil); 
     ... 
    }); 
}]; 

[operationQueue addOperation:operation]; 

私はあなたにもNSOperationQueueに操作オブジェクトを追加前提としています。その場合、キューは操作を保持しています。完了ブロックの実行中にもそれを保持している可能性があります(完了ブロックについての公式の確認は見つかりませんでしたが)。

しかし、完了ブロック内で別のブロックが作成されます。そのブロックは、NSOperationsの完了ブロックが最後まで実行された後に、ある時点で実行されます。この場合、operationはキューによって解放され、weakOperationnilになります。しかし、操作の完了ブロックから同じオブジェクトへの別の強力な参照を作成した場合は、ブロックによって実行されたoperationが存在することを確認し、ブロックによって変数operationが取り込まれないため、

AppleはTransitioning to ARC Release Notesでこの例を提供しています。の最後のコードスニペットを参照して、強い参照サイクルを回避するために寿命修飾子を使用してください。セクション。

+4

+1これは正解です。'NSOperation'は補完ブロックを保持しているので、補完ブロックで弱い参照を使用することは安全です。なぜなら、それはまだ生きていることが保証されているからです。しかし、OPの問題は、後で実行される2番目のブロックでそれを使用していることです。弱い参照がそこに存在することが保証されていません。正しい解決策は、第2のブロックに「NSOperation」に対する強い参照を持たせることである。 – user102008

4

受け入れられる回答が正しい。しかし、iOSの8/Mac OSの10.10のように、操作をweakifyする必要はありません。

NSOperation documentation on @completionBlockからの引用:iOSの8で

以降およびOS Xのv10 SP1の以降では、このプロパティを設定するには完了ブロックが実行を開始した後はゼロにします。

また、Pete Steinbergerのthis tweetも参照してください。

関連する問題