Objective-Cのブロックの使用法について少し混乱しています。私は現在ARCを使用していますが、私のアプリにはかなり多くのブロックがあり、現在は弱い参照ではなくself
を参照しています。これらのブロックがself
を保持し、それが解放されないようにすることが原因である可能性がありますか?問題は、ブロック内で常にweak
のself
参照を使用する必要があるかどうかということです。
-(void)handleNewerData:(NSArray *)arr
{
ProcessOperation *operation =
[[ProcessOperation alloc] initWithDataToProcess:arr
completion:^(NSMutableArray *rows) {
dispatch_async(dispatch_get_main_queue(), ^{
[self updateFeed:arr rows:rows];
});
}];
[dataProcessQueue addOperation:operation];
}
ProcessOperation.h
@interface ProcessOperation : NSOperation
{
NSMutableArray *dataArr;
NSMutableArray *rowHeightsArr;
void (^callback)(NSMutableArray *rows);
}
ProcessOperation.m
-(id)initWithDataToProcess:(NSArray *)data completion:(void (^)(NSMutableArray *rows))cb{
if(self =[super init]){
dataArr = [NSMutableArray arrayWithArray:data];
rowHeightsArr = [NSMutableArray new];
callback = cb;
}
return self;
}
- (void)main {
@autoreleasepool {
...
callback(rowHeightsArr);
}
}
ディスカッションのstrong
またはweak
の部分に焦点を合わせないようにするのに役立ちます。代わりにcycleの部分に注目してください。
保持cycleは、オブジェクトAがオブジェクトBを保持し、andオブジェクトBがオブジェクトAを保持しているときに発生するループです。
したがって、これら2つのオブジェクトは、プログラムの存続期間中、メモリ内でハングアップします。たとえすべてが正常に機能していれば、それらの割り当ては解除されます。
だから、私たちが心配しているのは保持cyclesであり、これらのサイクルを作成するブロック自体については何もありません。これは問題ではありません、例えば:
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
[self doSomethingWithObject:obj];
}];
ブロックはself
を保持しますが、self
はブロックを保持しません。どちらかが解放された場合、サイクルは作成されず、すべてが予定通りに割り当て解除されます。
どこに問題があるのでしょうか。
//In the interface:
@property (strong) void(^myBlock)(id obj, NSUInteger idx, BOOL *stop);
//In the implementation:
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[self doSomethingWithObj:obj];
}];
これで、あなたのオブジェクト(self
)はブロックへの明示的なstrong
参照を持ちます。そしてこのブロックはself
への暗黙的強い参照を持っています。それはサイクルです、そして今、どちらのオブジェクトも適切に割り当て解除されません。
このような状況では、self
定義上はすでにブロックへのstrong
参照を持っているので、ブロックを使用するために明示的に弱いself
への参照を作成することで解決するのが通常最も簡単です。
__weak MyObject *weakSelf = self;
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[weakSelf doSomethingWithObj:obj];
}];
しかし、self
を呼び出すブロックを扱う場合は、これが従うべきデフォルトパターンではありません。これは、そうでなければ自己とブロックの間の保持サイクルとなるものを破るためにのみ使用されるべきです。どこにでもこのパターンを採用するのであれば、self
の割り当てが解除された後に実行されたものにブロックを渡す危険性があります。
//SUSPICIOUS EXAMPLE:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
//By the time this gets called, "weakSelf" might be nil because it's not retained!
[weakSelf doSomething];
}];
あなたはいつも弱い参照を使う必要はありません。ブロックが保持されずに実行されてから破棄された場合は、保持サイクルが作成されないため、自分自身を強く捉えることができます。場合によっては、ブロックが完了するまでブロックを自己保持させたいので、途中で割り当てを解除しないようにします。しかし、あなたがブロックを強く捉え、内側に自分を捉えれば、それは保持サイクルを作り出すでしょう。
私は@jemmonsに完全に同意します。
「しかし、これはselfと呼ばれるブロックを扱うときに従うデフォルトのパターンであるべきではありません!これは、そうでなければselfとブロックの間の保持サイクルとなるものを破るために使われるべきです。 d自分自身の割り当てが解除された後に実行されたものにブロックを渡す危険性がある。」
//SUSPICIOUS EXAMPLE:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
//By the time this gets called, "weakSelf" might be nil because it's not retained!
[weakSelf doSomething];
}];
この問題を克服するために、ブロック内のweakSelfに対する強い参照を定義することができます。
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
MyObject *strongSelf = weakSelf;
[strongSelf doSomething];
}];
Leoが指摘するように、あなたがあなたの質問に追加したコードは強い参照サイクル(別名、保持サイクル)を示唆しないでしょう。強い参照サイクルを引き起こす可能性がある操作関連の問題の1つは、操作が解放されない場合です。あなたのコードスニペットはあなたがあなたの操作を同時と定義していないことを示唆しています、しかしあなたが持っているならば、あなたがisFinished
を決して投稿しなかったなら、操作が解除されないと、View Controllerも解除されません。操作のNSLog
メソッドにブレークポイントまたはdealloc
を追加して、それが呼び出されることを確認することをお勧めします。
あなたが言った:
私は保持サイクルの概念を理解しています、しかし、私はブロックで何が起こるのかよくわからないので、それは私を少し混乱させます
ブロックで発生する保持サイクル(強い参照サイクル)の問題は、あなたがよく知っている保持サイクルの問題と同じです。ブロックは、そのブロック内に現れるすべてのオブジェクトへの強い参照を維持し、ブロック自体が解放されるまでそれらの強い参照を解放しません。したがって、ブロックがself
を参照する場合、または単にself
のインスタンス変数を参照する場合でも、それはselfへの強い参照を維持し、ブロックが解放されるまで(またはこの場合はNSOperation
サブクラスが解放されるまで)解決されません。
詳細は、Objective-Cによるプログラミング:ブロックの操作ドキュメントの 自分自身をキャプチャするときは厳密な参照サイクルを避ける のセクションを参照してください。
それでもView Controllerが解放されない場合は、未解決の強力な参照が存在する場所を特定するだけです(NSOperation
の割り当てが解除されていることを確認したと仮定して)。一般的な例は、繰り返しのNSTimer
の使用です。あるいは、カスタムのdelegate
、または誤ってstrong
参照を管理している他のオブジェクト。インストゥルメントを使用して、オブジェクトが強力な参照を取得している場所を突き止めることができます。次に例を示します。
またはXcode 5の場合:
保持サイクルについての条件を無視している説明もあります。[オブジェクトのグループが強い関係の輪で結ばれている場合は、グループ外からの強い参照がなくてもそれらは相互に生き続けます。 文書