web-dev-qa-db-ja.com

このブロックで「自己」を強く捕捉すると、保持サイクルにつながる可能性があります

ブロックでリクエストがあります。しかし、コンパイラは警告を出します

「このブロックで「自己」を強く捉えると、保持サイクルにつながる可能性があります」

__weak typeof(self) weakSelf = self;
[generalInstaImage setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:data[@"images"][@"low_resolution"][@"url"]]] placeholderImage:[UIImage imageNamed:@"Default"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
    NSLog(@"success");
    [generalInstaImage setImage: image];
    [weakSelf saveImage:generalInstaImage.image withName:data[@"id"]];

    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
        NSLog(@"fail");
}];

私はweakSelf.generalInstaImageのような例の書き込みを試みますが、コンパイラはエラーを生成してコンパイルしません。

19
Alexander

この警告を考慮してください:

このブロックでselfを強くキャプチャすると、保持サイクルが発生する可能性があります

上記の警告を受け取ったら、ブロックについて次のことを確認する必要があります。

  • selfへの明示的な参照。または
  • インスタンス変数の参照によって引き起こされるselfへの暗黙の参照。

ブロックであったいくつかの単純なクラスプロパティがあると想像してみてください(これにより、質問と同じ「保持サイクル」警告が発生しますが、私の例は少し単純になります)。

@property (nonatomic, copy) void (^block)(void);

そして、ブロック内で使用したい他のクラスプロパティがあると仮定しましょう。

@property (nonatomic, strong) NSString *someString;

ブロック内でselfを参照すると(以下の私の例では、このプロパティにアクセスする過程で)、保持サイクルのリスクに関する警告が表示されます。

self.block = ^{
    NSLog(@"%@", self.someString);
};

それはあなたが提案したパターン、つまり次のように修正されます:

__weak typeof(self) weakSelf = self;

self.block = ^{
    NSLog(@"%@", weakSelf.someString);
};

あまり明白ではありませんが、ブロック内のクラスのインスタンス変数を参照すると、「保持サイクル」の警告も表示されます。次に例を示します。

self.block = ^{
    NSLog(@"%@", _someString);
};

これは、_someStringインスタンス変数がselfへの暗黙の参照を保持し、実際には以下と同等であるためです。

self.block = ^{
    NSLog(@"%@", self->_someString);
};

ここでも弱い自己パターンを採用しようとする傾向があるかもしれませんが、それはできません。 weakSelf->_someString構文パターンを試行すると、コンパイラーはこれについて警告します。

競合状態が原因でnull値が発生する可能性があるため、__weakポインターの逆参照は許可されていません。最初にstrong変数に割り当ててください

したがって、weakSelfパターンを使用してこれを解決しますが、ブロック内にローカルstrong変数を作成し、それを使用してインスタンス変数を逆参照します。

__weak typeof(self) weakSelf = self;

self.block = ^{
    __strong typeof(self) strongSelf = weakSelf;

    if (strongSelf) {
        NSLog(@"%@", strongSelf->_someString);

        // or better, just use the property
        //
        // NSLog(@"%@", strongSelf.someString);
    }
};

余談ですが、このブロック内でのローカルstrong参照strongSelfの作成には、他の利点もあります。つまり、完了ブロックが別のスレッドで非同期に実行されている場合は、ブロックの実行中にselfの割り当てが解除され、意図しない結果が生じることを心配する必要があります。

このweakSelf/strongSelfパターンは、ブロックプロパティを処理し、保持サイクル(強い参照サイクル)を防止したい場合に非常に役立ちますが、同時にself完了ブロックの実行中に割り当てを解除することはできません。

参考までに、Appleは、このパターンについて、さらに下の「重要なサイクル」の説明で説明しています 強力な参照サイクルを回避するためにライフタイム修飾子を使用するARCリリースノートへの移行


例でweakSelf.generalInstaImageを参照したときに「エラー」が発生したと報告しています。これはこの「保持サイクル」警告を解決する正しい方法です。そのため、何らかの警告を受け取った場合は、それを私たちと共有し、プロパティの宣言方法を示す必要があります。

61
Rob

__unsafe_unretained typeof(self) weakSelf = selfを使用する

2