4

私の iOS ARC 対応コードでは、「self」と他のオブジェクトをブロックに渡す必要があります。より具体的には、自分自身と の 内のオブジェクトを操作する必要がありASIHTTPRequestます。ASIHTTPRequestcompletionBlock

_operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(parseServerReply) object:nil];
_request = [ASIHTTPRequest requestWithURL:@"server.address"];

// ...

[_request setCompletionBlock:^{
    [self setResponseString:_request.responseString];
    [[MyAppDelegate getQueue] addOperation:_operation];
}];

次の警告を回避するには: Capturing "self" strongly in this block will likely lead to a retain cycle. __weakこの投稿に続くブロックで使用されるオブジェクトの属性を追加するようにコードを変更しました: ARC 対応コードでの警告「このブロックで [an object] を強力にキャプチャすると、保持サイクルが発生する可能性があります」を修正しました。

結果のコードは次のとおりです。

_operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(parseServerReply) object:nil];
_request = [ASIHTTPRequest requestWithURL:@"server.address"];

// ...
__weak NSOperation * operation = _operation;
__weak ASIHTTPRequest * request = _request;
__weak typeof(self) * self_ = self;

[request setCompletionBlock:^{
    [self_ setResponseString:request.responseString];
    [[MyAppDelegate getQueue] addOperation:operation];
}];

これでも保持サイクルとメモリ リークが発生する可能性があるかどうかを知りたいです。もしそうなら、漏れを避ける方法はありますか?

4

2 に答える 2

14

すべてを弱いものにする必要はありませんし、ブロック内の弱いオブジェクトを参照し続ける必要もありません (特にブロックが別のスレッドで実行できる場合)。

このように考えてください。ARC を使用する場合、オブジェクトは参照カウントされます。参照カウントがゼロになるまで、オブジェクトに対して dealloc は呼び出されません。したがって、参照が 1 つある限り、オブジェクトは存続します。

ただし、相互に強い参照を持つ 2 つのオブジェクトを考えてみましょう。どちらも、もう一方がその参照を解放するまで割り当てを解除しません。

ブロックを作成すると、その環境がキャプチャされます。つまり、ブロックのスコープで使用されるすべてのオブジェクトへの強い参照が作成されます。

それを念頭に置いて...

id object = getMeSomeObject();
// <object> is implicitly __strong, and now has a reference.

すべての強い参照が解放されるまで、オブジェクトは解放されません。ブロックで使用すると、ブロックは自動的に独自の強力な参照を作成して、ブロックが存続する限りオブジェクトが存続するようにします。

__weak 参照は、オブジェクトが生きている限りオブジェクトにアクセスできるレベルの間接参照です。基本的に、オブジェクトを __weak ポインターに割り当てると、そのオブジェクトが生きている限り、そのポインターは同じオブジェクトを与えることが保証されます。オブジェクトが独自の dealloc() を開始すると、すべての __weak ポインターが検出され、それらが nil に設定されます。

したがって、_weak ポインターは常に 2 つの状態のいずれかになります。オブジェクトが存在する限り有効なオブジェクトを指すか、オブジェクトが解放されている場合は nil です。弱いポインターを介してオブジェクトにアクセスしないでください。オブジェクトが背後で解放され、不良ポインターが残る可能性があるためです。

したがって、あなたがやりたいことは、スタック上に __strong 参照を作成して、オブジェクトが必要な限り生き続けるようにすることです。

あなたの場合...

[_request setCompletionBlock:^{
    [self setResponseString:_request.responseString];
    [[MyAppDelegate getQueue] addOperation:_operation];
}];

このブロックは明らかに への強い参照を保持していselfます。あなたはおそらくそれを望んでいません。修正してみましょう...

// weakSelf will be "magically" set to nil if <self> deallocs
__weak SelfType *weakSelf = self;
[_request setCompletionBlock:^{
    // If <self> is alive right now, I want to keep it alive while I use it
    // so I need to create a strong reference
    SelfType *strongSelf = weakSelf;
    if (strongSelf) {
        // Ah... <self> is still alive...
        [strongSelf setResponseString:_request.responseString];
        [[MyAppDelegate getQueue] addOperation:_operation];
    } else {
        // Bummer.  <self> dealloc before we could run this code.
    }
}];

ねえ、私たちは今弱い を持ってselfいますが...あなたはまだ同じ問題を抱えています. なんで?_request と _operation はインスタンス変数であるためです。ブロック内のインスタンス変数にアクセスすると、暗黙的に への強い参照が作成されselfます。

もう一度やり直して...

// weakSelf will be "magically" set to nil if <self> deallocs
__weak SelfType *weakSelf = self;
[_request setCompletionBlock:^{
    // If <self> is alive right now, I want to keep it alive while I use it
    // so I need to create a strong reference
    SelfType *strongSelf = weakSelf;
    if (strongSelf) {
        // Ah... <self> is still alive...
        [strongSelf setResponseString:strongSelf->_request.responseString];
        [[MyAppDelegate getQueue] addOperation:strongSelf->_operation];
    } else {
        // Bummer.  <self> dealloc before we could run this code.
    }
}];

さて、おそらく「そのまま」インスタンス変数を使用するべきではありませんが、それは別のトピックです。

これらの変更により、自己への強い参照を持たないブロックができ、実際に解放された場合、ブロックselfは適切に処理されます。

最後に、繰り返しますが、weakSelf のチェック後にオブジェクトが消えるという潜在的な問題を防ぐために、strongSelf への割り当てが必要です。具体的には...

if (weakSelf) {
    // Hey, the object exists at the time of the check, but between that check
    // and the very next line, its possible that the object went away.
    // So, to prevent that, you should ALWAYS assign to a temporary strong reference.
    [weakSelf doSomething];
}

strongSelf = weakSelf;
// OK, now IF this object is not nil, it is guaranteed to stay around as long as
// strongSelf lives.

さて、この場合、ブロックはリクエストの一部であるselfため、selfdeallocs の可能性は小さいですが、ここでの私の主なポイントは、保持サイクルを防ぐために self を使用することですが、それでも常に強い参照を介してオブジェクトにアクセスすることです――弱強のダンス。

于 2012-05-22T20:23:14.470 に答える
1

結果のコードは安全です。詳細については、Brad Larson の回答をご覧ください

于 2012-05-22T18:00:30.193 に答える