web-dev-qa-db-ja.com

UIViewControllerから呼び出された、保持されていない完了内で自分自身を参照するとき、weakSelf / strongSelfダンスは本当に必要ですか?

UIViewControllerサブクラス内に次のメソッドがあるとします。

- (void)makeAsyncNetworkCall
{
    [self.networkService performAsyncNetworkCallWithCompletion:^{
        dispatch_async(dispatch_get_main_queue(), ^{
                [self.activityIndicatorView stopAnimating];
            }
        });
    }];
}

ブロック内でselfを参照すると、ブロックによってUIViewControllerインスタンスが保持されることを知っています。 performAsyncNetworkCallWithCompletionNetworkServiceのプロパティ(またはivar)にブロックを保存しない限り、保持サイクルはないと私は思っていますか?

上記のこの構造により、UIViewControllerがperformAsyncNetworkCallWithCompletionが完了するまで保持されることに気づきます。しかし、システムは私のUIViewControllerの割り当てを解除する可能性があります(または可能ですか?)変更後iOS 6がUIViewControllerのバッキングCALayerメモリを管理する方法 )?

「weakSelf/strongSelfダンス」を実行する必要がある理由がある場合、次のようになります。

- (void)makeAsyncNetworkCall
{
    __weak typeof(self) weakSelf = self;
    [self.networkService performAsyncNetworkCallWithCompletion:^{
        typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) {
            return;
        }
        dispatch_async(dispatch_get_main_queue(), ^{
                [strongSelf.activityIndicatorView stopAnimating];
            }
        });
    }];
}

しかし、私はこれがひどく醜いので、必要がない場合は避けたいと思います。

39
Robert Atkins

正しく診断されたと思いますが、selfを使用しても、このシナリオでは強い参照サイクルが発生するとは限りません。ただし、これにより、ネットワーク操作が完了するまでView Controllerが保持されます。この場合(ほとんどの場合)、必要はありません。したがって、weakSelfを使用する必要はないかもしれませんが、おそらく使用するのが賢明です。偶発的な強い参照サイクルの可能性を最小限に抑え、メモリのより効率的な使用につながります(ネットワーク操作が完了するまでビューコントローラーを不必要に保持するのではなく、ビューコントローラーが閉じられるとすぐに、ビューコントローラーに関連付けられたメモリを解放します)。 。

ただし、strongSelf構文は必要ありません。あなたはできる:

- (void)makeAsyncNetworkCall
{
    __weak typeof(self) weakSelf = self;
    [self.networkService performAsyncNetworkCallWithCompletion:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            [weakSelf.activityIndicatorView stopAnimating];
        });
    }];
}

weakSelf/strongSelfの組み合わせが必要なのは、強い参照があることが重要な場合(たとえば、ivarを逆参照している場合)、または競合状態を心配する必要がある場合のみです。ここではそうではないようです。

30
Rob

問題は、networkServiceがブロックへの強い参照を保持する可能性があることだと思います。そして、ビューコントローラはnetworkServiceへの強い参照を持っているかもしれません。したがって、VC-> NetworkService-> block-> VCのpossibleサイクルが存在する可能性があります。ただし、この場合、通常、ブロックは実行後に解放されると想定しても安全です。この場合、サイクルが壊れます。したがって、この場合は必要ありません。

必要なのは、ブロックが解放されていない場合です。たとえば、ネットワーク呼び出しの後に1回実行されるブロックを用意する代わりに、コールバックとして使用されるブロックがあるとします。つまり、networkServiceオブジェクトはブロックへの強い参照を維持し、それをすべてのコールバックに使用します。この場合、ブロックにはVCへの強い参照があり、これにより強いサイクルが作成されるため、弱い参照が推奨されます。

7
Abizern

いいえ、self.networkServiceがブロックプロパティとして使用しない場合は問題ありません。

3
nanjunda

答えはここではそれほど簡単ではありません。 @Robの回答に同意しますが、追加の説明が必要です。

  1. __weakは安全な方法と考えられています。解放されたときに自己を無効にするためです。つまり、呼び出し元のオブジェクトがすでに解放されており、ブロックによって参照されているUIViewControllerのように、コールバックオブジェクトがすでに解放されている場合でも例外は発生しません。スタック。あらゆる種類の操作をキャンセルする可能性を追加することは、彼女は単に衛生上の問題であり、おそらくリソースの問題でもあります。たとえば、単にNSURLConnectionをキャンセルすることもできます。キャンセルできるのはNSOperationだけではなく、ブロックにコールバックするメソッドで非同期に実行されているものをキャンセルできます。

  2. Selfをブロックに保持させた場合、UIViewControllerのような呼び出し元オブジェクトがUINavigationControllerによって解放され、ブロックがそれを保持してコールバックすると、ストーリーは少し複雑になる可能性があります。この場合、コールバックブロックが実行され、その結果によって一部のデータが変更されると想定されます。それは振る舞いさえ欲しがるかもしれませんが、ほとんどの場合そうではありません。したがって、この場合は操作のキャンセルがより重要になる可能性があり、UINavigationControllerDelegateに関連付けられたオブジェクトまたはシングルトンとして存在する可変コレクションから非同期タスクをキャンセルすることにより、UINavigationControllerメソッドで操作を非常に賢くします。 。

もちろん、最初のオプションには賭けの保存が含まれますが、呼び出し元オブジェクトを閉じた後に非同期操作を続行したくない場合のみです。

0
mbpro