短い答え
self
直接アクセスする代わりに、保持されない参照から間接的にアクセスする必要があります。自動参照カウント(ARC)を使用していない場合、これを行うことができます。
__block MyDataProcessor *dp = self;
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
__block
ブロック内で変更することができ、キーワードマーク変数(私たちはそれをやっていない)が、ブロックが保持されている場合にも、それらは自動的に保持されません(あなたはARCを使用していない限り)。これを行う場合、MyDataProcessorインスタンスが解放された後、他に何もブロックを実行しようとしないことを確認する必要があります。(コードの構造を考えれば、それは問題にはなりません。)についての詳細を読んでください__block
。
ARCを使用している場合は、__block
変更のセマンティクスと参照が保持されます。その場合は宣言する必要があります__weak
。代わりにます。
長い答え
次のようなコードがあるとします。
self.progressBlock = ^(CGFloat percentComplete) {
[self.delegate processingWithProgress:percentComplete];
}
ここでの問題は、自己がブロックへの参照を保持していることです。一方、ブロックは、デリゲートプロパティをフェッチしてデリゲートにメソッドを送信するために、selfへの参照を保持する必要があります。アプリの他のすべてがこのオブジェクトへの参照を解放する場合、その保持カウントはゼロではなく(ブロックがそれを指しているため)、ブロックは何も問題を起こしていません(オブジェクトがそれを指しているため)など。オブジェクトのペアはヒープにリークし、メモリを占有しますが、デバッガなしでは永遠に到達できません。悲劇、本当に。
その場合は、代わりに次のようにすることで簡単に修正できます。
id progressDelegate = self.delegate;
self.progressBlock = ^(CGFloat percentComplete) {
[progressDelegate processingWithProgress:percentComplete];
}
このコードでは、selfはブロックを保持し、ブロックはデリゲートを保持しており、サイクルはありません(ここから表示されます。デリゲートはオブジェクトを保持する可能性がありますが、現時点では手に負えません)。デリゲートプロパティの値は、ブロックの実行時に検索されるのではなく、ブロックの作成時にキャプチャされるため、このコードは同じようにリークの危険を冒しません。副作用として、このブロックの作成後にデリゲートを変更しても、ブロックは引き続き古いデリゲートに更新メッセージを送信します。それが発生する可能性があるかどうかは、アプリケーションによって異なります。
あなたがその振る舞いにクールだったとしても、あなたのケースではまだそのトリックを使うことはできません:
self.dataProcessor.progress = ^(CGFloat percentComplete) {
[self.delegate myAPI:self isProcessingWithProgress:percentComplete];
};
ここでself
は、メソッド呼び出しでデリゲートに直接渡しています。そのため、どこかでデリゲートを取得する必要があります。ブロックタイプの定義を制御できる場合は、デリゲートをパラメーターとしてブロックに渡すことをお勧めします。
self.dataProcessor.progress = ^(MyDataProcessor *dp, CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
};
このソリューションは、保持サイクルを回避し、常に現在のデリゲートを呼び出します。
ブロックを変更できない場合は、対処できます。保持サイクルがエラーではなく警告である理由は、それらが必ずしもアプリケーションの運命を綴るとは限らないためです。MyDataProcessor
が操作の完了時にブロックを解放できる場合、親がブロックを解放しようとする前に、サイクルが中断され、すべてが適切にクリーンアップされます。これが確実である場合、正しいこと#pragma
は、コードブロックの警告を抑制するためにa を使用することです。(または、ファイルごとのコンパイラフラグを使用します。ただし、プロジェクト全体の警告を無効にしないでください。)
また、上記と同様のトリックを使用して、参照を弱または非保持として宣言し、それをブロックで使用することも検討できます。例えば:
__weak MyDataProcessor *dp = self; // OK for iOS 5 only
__unsafe_unretained MyDataProcessor *dp = self; // OK for iOS 4.x and up
__block MyDataProcessor *dp = self; // OK if you aren't using ARC
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
上記の3つはすべて、結果を保持せずに参照を提供しますが、それらはすべて少し異なる動作__weak
をします。オブジェクトが解放されたときに参照をゼロにしようとします。__unsafe_unretained
無効なポインタが残ります。__block
実際には別のレベルの間接参照が追加され、ブロック内から参照の値を変更できるようになります(この場合、dp
他の場所では使用されないため、関係ありません)。
何が最適かは、変更できるコードと変更できないコードによって異なります。しかし、うまくいけば、これがあなたにどのように進むべきかについていくつかの考えを与えてくれました。