14

弱いオブジェクトにメッセージを送信するとどうなりますか?メッセージの送信はオブジェクトを所有し、返されるまでそれをメモリに保持しますか?

私はこのパターンを考えています:

__weak MyObject *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
    [weakSelf doSomeAction];
});

weakSelfメッセージが送信されたときにnil以外であると仮定すると、メッセージは作業中に割り当てが解除される可能性がありますか、それとも返さdoSomeActionれるまで有効であることが保証されますか?doSomeAction

4

2 に答える 2

20

Clang ARCのドキュメントから:

読み取りは、オブジェクトの左辺値に対して左辺値から右辺値への変換を実行するときに発生します。

  • オブジェクトの場合__weak、現在のポインティは保持され、現在の完全な式の最後に解放されます。これは、割り当てとポインティの最終リリースに関してアトミックに実行する必要があります。

弱参照をメッセージングすると、変数に対して左辺値から右辺値への変換が実行されます。つまり、弱参照の値が保持され、現在の完全式(基本的にはステートメント)の最後に解放されます。これは基本的に、スコープが現在のステートメントに対してのみ存続する強力な変数に割り当ててから、その強力な変数にメッセージを送信することと同じです。

nilここでのポイントは、弱変数に一度メッセージを送り、二度と触れないようにし、弱参照が終わった場合にメソッドへの引数を評価することの副作用を気にしない場合です。先に、弱参照に直接メッセージを送信します。ただし、弱参照を2回(別々のステートメントで)参照する必要がある場合、または引数を評価することの副作用が重要な場合は、nil先に進む前に、強変数に割り当てて非をテストする必要があります。

于 2013-02-22T21:47:59.250 に答える
5

あなたは尋ねました:

メッセージが送信されたときにweakSelf非割り当てであると仮定すると、メッセージは作業中に割り当てが解除される可能性がありますか、それとも返されるまで有効であることが保証されますか?nildoSomeActiondoSomeAction

このARCの動作は時間の経過とともに変化しました。しかし、最近ではweak、最後の強力な参照が削除されるとすぐに参照を解放できます。

したがって、次のことを考慮してください。

- (void)dealloc {
    NSLog(@"%s", __FUNCTION__);
}

- (void)startBackgroundOperation {
    __weak typeof(self) weakSelf = self;
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [weakSelf doSomeAction];
        [NSThread sleepForTimeInterval:5];
        [weakSelf doSomeAction2];
    });
}

- (void)doSomeAction {
    NSLog(@"%s", __FUNCTION__);
}

- (void)doSomeAction2 {
    NSLog(@"%s", __FUNCTION__);
}

コードを呼び出しstartBackgroundOperationて、との間の間にオブジェクトの割り当てを解除するdoSomeActiondoSomeAction2、前者が呼び出され、後者は呼び出されないことがわかります。つまり、強力な参照がなくなった場合、オブジェクトはブロックの中央で割り当て解除される可能性があります。

したがって、弱い参照が必要であるが、それがクロージャの期間中保持される「全か無かの」種類の動作が必要な場合は、冗談めかして「<code>weakSelf- strongSelfdance」と呼ばれるものを実行します。

- (void)startBackgroundOperation {
    __weak typeof(self) weakSelf = self;

     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        typeof(self) strongSelf = weakSelf;        // establish just-in-time strong reference (if `weakSelf` is not yet `nil`)

        [strongSelf doSomeAction];
        [NSThread sleepForTimeInterval:5];
        [strongSelf doSomeAction2];
    });
}

これにより、ブロックに参照があることが保証weakされますが、の割り当てに達するまでに割り当てが解除されない場合strongSelfは、ブロックの期間中、強力な参照が確立および維持されます。


価値があるのは、このパターンは、 weakSelf(で競合状態を回避する)strongSelfでivarを逆参照するときに不可欠です。->weakSelf

例えば

- (void)badDeferenceExample {
    __weak typeof(self) weakSelf = self;

     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if (!weakSelf) { return; }

        NSInteger value = weakSelf->_someIVar;     // this is race and can crash!!!

        ...
    });
}

- (void)properDeferenceExample {
    __weak typeof(self) weakSelf = self;

     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        typeof(self) strongSelf = weakSelf;        // establish just-in-time strong reference (if `weakSelf` is not yet `nil`)

        if (!strongSelf) { return; }

        NSInteger value = strongSelf->_someIVar;   // this is safe

        ...
    });
}
于 2013-02-23T03:53:59.743 に答える