17

質問をよりよく説明するために、次の簡略化された形式のブロック再帰を検討してください。

__block void (^next)(int) = ^(int index) {
    if (index == 3) {
        return;
    }
    int i = index;
    next(++i);
};
next(0);

XCode(ARC対応)は、「このブロックで「次へ」を強くキャプチャすると、保持サイクルが発生する可能性が高い」と警告しています。

同意しました。

質問1nil :次のように、ブロック自体をに設定することで、保持サイクルを正常に中断できますか?

__block void (^next)(int) = ^(int index) {
    if (index == 3) {
        next = nil; // break the retain cycle
        return;
    }
    int i = index;
    next(++i);
};
next(0);

(注:同じ警告が表示されますが、おそらくそれは保証されていません)

質問2:ブロック再帰のより良い実装は何でしょうか?

ありがとう。

4

2 に答える 2

5

保持サイクルのない再帰的なブロック実行を実現するには、2つのブロック参照を使用する必要があります。1つは弱い、もう1つは強いです。したがって、あなたの場合、これはコードがどのように見えるかです:

__block __weak void (^weak_next)(int);
void (^next)(int);
weak_next = next = ^(int index) {
  if (index == 3) {
    return;
  }
  int i = index;
  weak_next(++i);
};
next(0);

ブロックは弱いブロック参照(weak_next)をキャプチャし、外部コンテキストは強い参照(next)をキャプチャしてブロックを維持することに注意してください。両方の参照が同じブロックを指しています。

このパターンの別の例については、 https://stackoverflow.com/a/19905407/1956124を参照してください。これもブロック再帰を使用します。さらに、次の記事のコメントセクションでの議論もここに関連しています:http://ddeville.me/2011/10/recursive-blocks-objc/

于 2014-10-30T16:35:37.887 に答える
1

@MattWildingのソリューションについては@newacctが正しいと思います。その場合、次のブロックへの強い参照を持つものはなく、実行時に実行時例外が発生するようです(少なくとも私にとってはそうです)。

objcで再帰的に呼び出されるブロックを実際に見つけることがどれほど一般的かはわかりません。ただし、実際の実装(実際に必要な場合)、たとえばビューコントローラでは、ブロックを定義してから、そのブロックへの強力な参照を使用して内部インターフェイスプロパティを設定できます。

typedef void(^PushButtonBlock)();

@interface ViewController ()
@property (strong, nonatomic) PushButtonBlock pushButton;
@end

@implementation ViewController
  ... 
  // (in viewDidLoad or some such)
  __weak ViewController *weakSelf = self;

  self.pushButton = ^() {
    [weakSelf.button pushIt];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), weakSelf.pushButton);
  };

  self.pushButton();
  ...
@end

これは私にとっては問題なく動作し、保持サイクルに関するコンパイラの警告はありません(そして機器のリークもありません)。しかし、私はおそらくobjcでこれ(再帰的なブロック呼び出し)を行うことを避けたいと思います-それは臭いです。しかし、いずれにせよ興味深い。

于 2013-02-13T07:58:33.427 に答える