8

selfブロック内からアクセスしようとしているとしましょう:

[someObject successBlock:^(NSArray *result) {
    [self someSuccessMethod];
} failure:^(NSString *errorMessage, int status) {
    [self someFailureMethod];
}];

これにより保持サイクルが発生し、割り当てが解除さsomeObjectれないことを理解しています。self

私を混乱させているのは、__blockキーワードの有無にかかわらず実際に何が起こるかです。__weak自分自身への参照を作成することで、保持サイクルを修正できます。

__weak MyClass* me = self;
[someObject successBlock:^(NSArray *result) {
    [me someSuccessMethod];
} failure:^(NSString *errorMessage, int status) {
    [me someFailureMethod];
}];

ブロック内__blockから変更しようとしているわけではないので、ここで使用する必要はありません。me私が理解していることから、 を使用しない場合__block、 のコピーがmeブロック内で参照されます。私の質問は、ブロック内で参照されているのがオブジェクトの単なるコピーである場合、元のコード ブロックが保持サイクルを作成するのはなぜですか? 私はキーワードselfを使用していないので、への参照は単なるコピーであると推測します。__block私はこれについて間違って考えていますか?

4

4 に答える 4

4

保持サイクルは、2 つのオブジェクトが相互に強い参照を格納するときに発生します。最も単純なケースは、オブジェクトaへの強い参照を格納し、反対のことbb行うオブジェクトです [1]。保持サイクルは、これらのオブジェクトが他の場所から参照されていない場合でも、これらのオブジェクトが常に使用されていると ARC に認識させるため、Objective-C の問題です。

いくつかの例を見てみましょう。zとを割り当てabそれらを利用してから破棄するオブジェクトがあります。そもそも と の間で保持サイクルが作成され、a割り当てが解除されない場合。それを数回行うと、深刻なメモリリークが発生します。bab

保持サイクルのもう 1 つの実例は、 ifがオブジェクトaを割り当てて強く参照する場合ですが、 tobからの強い参照も格納します(オブジェクト グラフ内の多くの小さなオブジェクトは、それらの親にアクセスする必要がある場合があります)。ba

これらの場合の最も一般的な解決策は、含まれているオブジェクトが含まれているオブジェクトへの弱い参照のみを持つようにし、兄弟オブジェクトが相互に強い参照を含まないようにすることです。

別の解決策 (一般的には洗練されていませんが、状況によっては適切かもしれません) は、 への参照を nil する何らかのカスタムcleanupメソッドを持つことができます。したがって、が呼び出されたときに割り当てが解除されます (他の場所で強く参照されていない場合)。からこれを行うことができず(保持サイクルがある場合は呼び出されない)、適切なタイミングで呼び出すことを覚えておく必要があるため、これは面倒です。abbcleanupbadealloccleanup

  1. リテイン サイクルもまた推移的であることに注意してください (たとえば、オブジェクトaは強く参照bし、強く参照するオブジェクトは強く参照しcますa)。

とはいえ、ブロックのメモリ管理は非常に理解しにくいものです。

最初の例では、一時的な保持サイクルを作成できます (selfオブジェクトが への強い参照を格納している場合のみsomeObject)。この一時保持サイクルは、ブロックの実行が終了して割り当てが解除されるとなくなります。

実行中、 への参照、への参照、およびへの参照が再びself格納されます。ただし、ブロックは永続的にどこにも格納されないため、これは一時的なものにすぎません (実装がそうしない限り、完了ブロックでは頻繁ではありません)。someObjectsomeObjectblockblockself[someObject successBlock:failure:]

したがって、最初の例では保持サイクルは問題になりません。

一般に、ブロック内のリテイン サイクルが問題になるのは、オブジェクトがブロックを直接実行するのではなく、ブロックを格納している場合のみです。次に、 が を強く参照し、がをself強く参照していることを簡単に確認できます。ブロック内からivarにアクセスすると、そのブロック内でへの強い参照が自動的に生成されることに注意してください。blockblockselfself

含まれているオブジェクトがコンテナーを強く参照しないようにするのと同じことは__weak SelfClass *weakSelf = self、メソッドと ivar の両方にアクセスするために使用されます (プロパティを使用する場合のように、アクセサーを介して ivar にアクセスする場合に適しています)。ブロックの参照selfは弱くなり (それはコピーではなく、弱い参照です)、self強く参照されなくなったときに割り当てを解除できます。

weakSelf念のため、格納されているかどうかにかかわらず、すべてのブロック内で常に使用することをお勧めします。Apple がこれをデフォルトの動作にしなかったのはなぜだろうか。これを行うことは、実際には不要であっても、ブロック コードに害を及ぼすことはありません。


__blockObjective-C はそのようなオブジェクトの不変性を強制しないため、オブジェクトを指す変数ではめったに使用されません。

オブジェクトへのポインターがある場合は、そのメソッドを呼び出すことができ、これらのメソッドは、 の有無にかかわらずオブジェクトを変更でき__blockます。__block基本型 (int、float など) の変数でより (のみ?) 役立ちます。オブジェクトポインタ変数で使用するとどうなるかについては、こちらを参照してください。Apple によるBlocks Programming Topics__block詳細を読むこともできます。__block

編集:__blockオブジェクト ポインターの使用に関する間違いを修正しました。それを指摘してくれた@KevinDiTragliaに感謝します。

于 2013-07-23T16:04:07.350 に答える
3

最初の例では、終わりのない保持サイクルは作成されません。保持サイクルがありますが、ブロックが完了すると、ブロックからの参照someObjectが削除されます。したがって、someObject少なくともブロックが完了するまでは存続します。このような一時的な保持サイクルは、必要に応じて、良いことも悪いこともあります。

someObject少なくともブロックが完了するまで生きている必要がある場合は、問題ありません。ただし、そのオブジェクトを保持する理由がない場合は、「弱い」参照を使用して実装する必要があります。

例えば。myObject は、これらのブロックでネットから画像を取得するビュー コントローラーです。ナビゲーションコントローラーからそれをポップするsomeObjectと、コントローラーは画像を取得した後に画像を表示できないため、画像を保持する必要はありません。成功またはエラーは関係ありません。ユーザーはsomeObject取得するはずだった画像に興味を持っていません。このような場合、weak を使用する方が適切なオプションですが、ブロック内のコードはselfnil よりも期待する必要があります。

于 2014-02-16T15:29:27.467 に答える