5

次の 2 つのユーティリティ関数を作成しました。

+ (void)dispatch:(void (^)())f afterDelay:(float)delay {
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay*NSEC_PER_SEC)),
                    dispatch_get_main_queue(),
                    f);
  }

+ (void)dispatch:(void (^)())f withInterval:(float)delay {
    void (^_f)() = nil; // <-- A
    _f = ^{
        f();
        [self dispatch:_f afterDelay:delay]; // <-- B
    };
    [self dispatch:_f afterDelay:delay];
}

アイデアは、あなたが呼び出すことができるということです:

[自己ディスパッチ:ブロックafterDelay:遅延]; - 特定の時間後に実行されるブロックを取得する

[自己ディスパッチ:ブロックwithInterval:遅延]; - ブロックを定期的に実行する

さて、 dispatch:withInterval:をそのまま呼び出すと、プログラムがBの行を実行しようとすると_fの値がnilになるため、実行時にエラーが発生します。_f が A の _fの参照を保持しているため、これが順番に発生します。

Aを次のように変更すると、これを修正できます。

__block void (^_f)() = nil;

これにより、 _fへの強い参照を作成しているため、コードがBに到達すると、 _fの値がそれに割り当てられた最終的な値になります。これに関する問題は、保持サイクルが発生していることです。

最後に、Aを次のように変更できます。

__block void (^_f)() __weak = nil;

それは両方の問題を処理する必要がありますが、コードがBに達すると、 _fの値が再びnilになることがわかりました。これは、評価される時点で、_fが既に割り当て解除されているためです。

いくつか質問があります。

  • 最後のシナリオで、 _fの割り当てが解除されるのはなぜですか? 少なくとも次のディスパッチ呼び出しまでブロックを保持するように ARC に指示するにはどうすればよいですか?
  • これらの関数を記述するための最良の (および ARC 準拠の) 方法は何でしょうか?

御時間ありがとうございます。

4

2 に答える 2

1

_fそうしないと、ARC で割り当てられたブロックがすぐに消えてしまう可能性があるためです。これは、強い参照がないためです。

同時に、ブロックはそれ自体へのポインターにアクセスする必要があり、発見したように、これは__block変数で行う必要があります。ブロックからそれ自体への強い参照は保持サイクルを引き起こすため、これは弱い参照でなければなりません。

したがって、強い変数と弱い変数の 2 つの変数が必要です。

+ (void)dispatch:(void (^)())f withInterval:(float)delay {
    __block __weak void (^_f_weak)() = nil; // a weak __block variable for the block to capture
    void (^_f)() = nil; // a strong variable to hold the block itself
    _f_weak = _f = ^{ // both variables will point to the block
        f();
        [self dispatch:_f_weak afterDelay:delay];
    };
    [self dispatch:_f afterDelay:delay];
}
于 2013-10-28T02:45:34.850 に答える