保持サイクルは、2 つのオブジェクトが相互に強い参照を格納するときに発生します。最も単純なケースは、オブジェクトaへの強い参照を格納し、反対のことbをb行うオブジェクトです [1]。保持サイクルは、これらのオブジェクトが他の場所から参照されていない場合でも、これらのオブジェクトが常に使用されていると ARC に認識させるため、Objective-C の問題です。
いくつかの例を見てみましょう。zとを割り当てa、bそれらを利用してから破棄するオブジェクトがあります。そもそも と の間で保持サイクルが作成され、a割り当てが解除されない場合。それを数回行うと、深刻なメモリリークが発生します。bab
保持サイクルのもう 1 つの実例は、 ifがオブジェクトaを割り当てて強く参照する場合ですが、 tobからの強い参照も格納します(オブジェクト グラフ内の多くの小さなオブジェクトは、それらの親にアクセスする必要がある場合があります)。ba
これらの場合の最も一般的な解決策は、含まれているオブジェクトが含まれているオブジェクトへの弱い参照のみを持つようにし、兄弟オブジェクトが相互に強い参照を含まないようにすることです。
別の解決策 (一般的には洗練されていませんが、状況によっては適切かもしれません) は、 への参照を nil する何らかのカスタムcleanupメソッドを持つことができます。したがって、が呼び出されたときに割り当てが解除されます (他の場所で強く参照されていない場合)。からこれを行うことができず(保持サイクルがある場合は呼び出されない)、適切なタイミングで呼び出すことを覚えておく必要があるため、これは面倒です。abbcleanupbadealloccleanup
- リテイン サイクルもまた推移的であることに注意してください (たとえば、オブジェクト
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に感謝します。