4

私はこれを正しく行うかどうかを理解しようとしています:

ブロックが 1 つある場合は、次のようにします。

__weak MyClass *weakSelf = self;  
[self performBlock:^{                 //<< Should I use self, or weakSelf here?

    [weakSelf doSomething];

} afterDelay:delay];

しかし、ブロックの中にブロックがあるとどうなるでしょうか? これは正しいでしょうか?

__weak MyClass *weakSelf = self;
[self performBlock:^{

    [weakSelf doSomething];

    [self performBlock:^{

        [weakSelf doSomething]; 
    } afterDelay:1.0f];

} afterDelay:delay];

また、以下の機能で【ブロックコピー】を使う必要はありますか?

- (void)performBlock:(void (^)(void))block afterDelay:(float)delay
{
    if (block)
    {
        if (delay > 0)
        {
            [self performSelector:@selector(executeBlockAfterDelay:) withObject:[block copy] afterDelay:delay];
        }
        else
        {
            [self executeBlockAfterDelay:[block copy]];
        }
    }
}

- (void)executeBlockAfterDelay:(void(^)(void))block
{
    if (block)
        block();
}
4

4 に答える 4

5

この場合(以下)は、ブロックがそれらの数秒間だけコピーされるため、strongを使用します。 selfそして通常、selfブロックを実行したい場合は、その時まで生き続けたいので、強い参照は完全に大丈夫です。

[self performBlock:^{
    [self doSomething]; // strong is OK
} afterDelay:delay];

ブロック内のブロック?あなたの場合、これらの2つのブロックは遅延ワンショットブロックであるため、上記と同じように、strongを使用します。ただし、ブロック間には違いがあります。ブロックを長期間保存する場合、おそらく複数の呼び出しの場合は、retain-cyclesを避ける必要があります。

例:

self.callback = ^{
    [self doSomething]; // should use weakSelf
};

これにより、保持サイクルが発生する可能性があります。実際、それはブロックがどのように使用されるかに依存します。後で使用するために、ブロックがプロパティに保存(コピー)されていることがわかります。ただし、使用されなくなるブロックを無効にすることで、保持サイクルを防ぐことができます。この場合:

self.callback(); //invoke
self.callback = nil; //release

ARCを使用する場合、ブロックを自分でコピーする必要はありません。ブロックが追加された後の初期のバージョンにはバグがありましたが、現在、ARCのコンパイラはブロックをコピーするタイミングを認識しています。この場合、それをコピーするのに十分賢いです:

[self performSelector:@selector(executeBlockAfterDelay:) withObject:block afterDelay:delay];
于 2013-03-17T10:48:53.260 に答える
5

を実装するのではなく-performBlock:afterDelay:、単に使用しますdipatch_after()。とりわけ、これはオブジェクトに配信されるメッセージではないため、どの受信者をターゲットにするかは問題ありません。

実際、ここにはメモリ管理の問題はまったくありません。通常、オブジェクトがブロックを保持し、ブロックが (おそらく暗黙のうちに) 同じオブジェクトを保持する場合にのみ、「弱い自己」アプローチを実行する必要があります。ただし、オブジェクトはブロックを保持していません。-performSelector:withObject:afterDelay:火災が発生するまでフレームワークによって保持されますが、それは保持サイクルではありません。

保持サイクルがあった場合はself、ブロック内で参照しないでください。selfしたがって、ネストされたケースは、ではなくでメッセージを呼び出すのに間違っていますweakSelf

最後に、はい、[block copy]実行がその宣言のスコープを離れた後、またはそれを非ブロック固有の API に渡す場合にブロックを保持する場合は常に必要です。つまり、必要にdispatch_async()応じて独自のコピーを作成することを認識しているブロック対応 API であるため、ブロックを渡すときにブロックをコピーする必要はありません。ただし-performSelector:withObject:afterDelay:、ブロック対応ではありません。引数を汎用オブジェクトとして扱い、保持します。そのため、ブロックを渡すときにブロックをコピーする必要があります。

于 2013-03-16T11:38:48.090 に答える
0

ブロックについて理解しておくべき最も重要なことの 1 つは、ブロックがコードの一部 (値を含む) を抽象的なエンティティーにキャプチャーし、それをアトミック オブジェクトとして操作できることです (どこかに保持する、渡す、コピーするなど...)。実際には、デフォルトでブロックが有効であり、後で安全に実行できることを保証する方法で実装されています。

次に、必要な依存関係をブロック内にキャプチャして保持する必要があります。

残念ながら、場合によっては (実際には非常に多くの場合)、ブロックはそれを作成したインスタンスによって保持され、そのインスタンス自体が保持されます。これは保持ループと呼ばれ、保持関係のいずれかを自分で解除しない限り、オブジェクトとブロックの割り当てを解除できなくなります。これは、たとえばインスタンス変数を使用してブロックを参照し、手動で nilify しない場合に発生する可能性があります。

これはおそらくブロックの主な問題です。特に、ブロックが自己インスタンスを保持していることがわからない場合があるためです (たとえば、ブロック内の NSAsert など)。それで:

  • ブロックをすぐに実行して解放する場合 (実行後にディスパッチ解放でブロックを使用する)、self によって参照されるオブジェクトがまだ存在することが確実であるため、リスクはありません。

  • ただし、実行が遅れる場合は、オブジェクトをブロック内に保持することが重要です。ただし、その場合、保持ループを回避するために、オブジェクトはブロックを保持しないようにする必要があります (A は B を保持し、B は A を保持します)。メソッドのプライベートスコープでブロックを定義し、必要に応じて参照すると、完璧です。

  • コピーについて。はい、ブロックがメソッド引数として渡された場合、copy を使用する方が安全です。このスコープ内に +1 の保持カウントを持つクリーンな排他的ブロックがあることを確認してください。しかし、おそらくARCはすでにあなたのためにそれを行っています. それについてはわかりません。たとえば、performWithSelector は無料で行うようで、コピーは危険ではありません。ただの駄目。コンパイラはそれを削除して最適化できる場合がありますが、チェックする必要があります。

于 2013-03-16T11:52:28.293 に答える
-3

私は通常これを行います:

__unsafe_unretained __block id blockSelf = self;

そして、それを私のブロックで問題なく使用します。

だからあなたの場合:

__unsafe_unretained __block MyClass *blockSelf = self;
[self performBlock:^{
    [weakSelf doSomething];
    [self performBlock:^{
        [weakSelf doSomething]; 
    } afterDelay:1.0f];
} afterDelay:delay];

また、あなたの人生を少し楽にするために-ユーティリティクラスを作成し、これをヘッダーに入れます

void RunAfterDelay(NSTimeInterval delayInSeconds, dispatch_block_t block);

そして、これを .m ファイルに

void RunAfterDelay(NSTimeInterval delayInSeconds, dispatch_block_t block)
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC), dispatch_get_main_queue(), block);
}

ユーティリティをプレフィックスにインポートすると、次のことができます。

__unsafe_unretained __block MyClass *blockSelf = self;
RunAfterDelay(1.0f,^{
    [blockSelf doSomething];
    RunAfterDelay(delay,^{
        [blockSelf doSomething];
    })
});

冗長なデフォルトのものよりも読みやすいと思います。

お役に立てれば :)

于 2013-03-16T11:36:08.513 に答える