web-dev-qa-db-ja.com

ARCでは常に弱い自己参照をブロックに渡しますか?

Objective-Cのブロックの使用法について少し混乱しています。私は現在ARCを使用していますが、私のアプリにはかなり多くのブロックがあり、現在は弱い参照ではなくselfを参照しています。これらのブロックがselfを保持し、それが解放されないようにすることが原因である可能性がありますか?問題は、ブロック内で常にweakself参照を使用する必要があるかどうかということです。

-(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);
    }
}
244
the_critic

ディスカッションのstrongまたはweakの部分に焦点を合わせないようにするのに役立ちます。代わりにcycleの部分に注目してください。

保持cycleは、オブジェクトAがオブジェクトBを保持し、andオブジェクトBがオブジェクトAを保持しているときに発生するループです。

  • オブジェクトBはその参照を保持しているので、オブジェクトAは解放されません。
  • しかし、オブジェクトAがそれを参照している限り、オブジェクトBは解放されません。
  • しかし、オブジェクト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];
}];
687
jemmons

あなたはいつも弱い参照を使う必要はありません。ブロックが保持されずに実行されてから破棄された場合は、保持サイクルが作成されないため、自分自身を強く捉えることができます。場合によっては、ブロックが完了するまでブロックを自己保持させたいので、途中で割り当てを解除しないようにします。しかし、あなたがブロックを強く捉え、内側に自分を捉えれば、それは保持サイクルを作り出すでしょう。

25
Leo Natan

私は@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];
}];
23
Ilker Baltaci

Leoが指摘するように、あなたがあなたの質問に追加したコードは強い参照サイクル(別名、保持サイクル)を示唆しないでしょう。強い参照サイクルを引き起こす可能性がある操作関連の問題の1つは、操作が解放されない場合です。あなたのコードスニペットはあなたがあなたの操作を同時と定義していないことを示唆しています、しかしあなたが持っているならば、あなたがisFinishedを決して投稿しなかったなら、操作が解除されないと、View Controllerも解除されません。操作のNSLogメソッドにブレークポイントまたはdeallocを追加して、それが呼び出されることを確認することをお勧めします。

あなたが言った:

私は保持サイクルの概念を理解しています、しかし、私はブロックで何が起こるのかよくわからないので、それは私を少し混乱させます

ブロックで発生する保持サイクル(強い参照サイクル)の問題は、あなたがよく知っている保持サイクルの問題と同じです。ブロックは、そのブロック内に現れるすべてのオブジェクトへの強い参照を維持し、ブロック自体が解放されるまでそれらの強い参照を解放しません。したがって、ブロックがselfを参照する場合、または単にselfのインスタンス変数を参照する場合でも、それはselfへの強い参照を維持し、ブロックが解放されるまで(またはこの場合はNSOperationサブクラスが解放されるまで)解決されません。

詳細は、Objective-Cによるプログラミング:ブロックの操作ドキュメントの 自分自身をキャプチャするときは厳密な参照サイクルを避ける のセクションを参照してください。

それでもView Controllerが解放されない場合は、未解決の強力な参照が存在する場所を特定するだけです(NSOperationの割り当てが解除されていることを確認したと仮定して)。一般的な例は、繰り返しのNSTimerの使用です。あるいは、カスタムのdelegate、または誤ってstrong参照を管理している他のオブジェクト。インストゥルメントを使用して、オブジェクトが強力な参照を取得している場所を突き止めることができます。次に例を示します。

record reference counts in Xcode 6

またはXcode 5の場合:

record reference counts in Xcode 5

19
Rob

保持サイクルについての条件を無視している説明もあります。[オブジェクトのグループが強い関係の輪で結ばれている場合は、グループ外からの強い参照がなくてもそれらは相互に生き続けます。 文書

0
Danyun Liu