保持サイクルは、2 つのオブジェクトが相互に強い参照を格納するときに発生します。最も単純なケースは、オブジェクトa
への強い参照を格納し、反対のことb
をb
行うオブジェクトです [1]。保持サイクルは、これらのオブジェクトが他の場所から参照されていない場合でも、これらのオブジェクトが常に使用されていると ARC に認識させるため、Objective-C の問題です。
いくつかの例を見てみましょう。z
とを割り当てa
、b
それらを利用してから破棄するオブジェクトがあります。そもそも と の間で保持サイクルが作成され、a
割り当てが解除されない場合。それを数回行うと、深刻なメモリリークが発生します。b
a
b
保持サイクルのもう 1 つの実例は、 ifがオブジェクトa
を割り当てて強く参照する場合ですが、 tob
からの強い参照も格納します(オブジェクト グラフ内の多くの小さなオブジェクトは、それらの親にアクセスする必要がある場合があります)。b
a
これらの場合の最も一般的な解決策は、含まれているオブジェクトが含まれているオブジェクトへの弱い参照のみを持つようにし、兄弟オブジェクトが相互に強い参照を含まないようにすることです。
別の解決策 (一般的には洗練されていませんが、状況によっては適切かもしれません) は、 への参照を nil する何らかのカスタムcleanup
メソッドを持つことができます。したがって、が呼び出されたときに割り当てが解除されます (他の場所で強く参照されていない場合)。からこれを行うことができず(保持サイクルがある場合は呼び出されない)、適切なタイミングで呼び出すことを覚えておく必要があるため、これは面倒です。a
b
b
cleanup
b
a
dealloc
cleanup
- リテイン サイクルもまた推移的であることに注意してください (たとえば、オブジェクト
a
は強く参照b
し、強く参照するオブジェクトは強く参照しc
ますa
)。
とはいえ、ブロックのメモリ管理は非常に理解しにくいものです。
最初の例では、一時的な保持サイクルを作成できます (self
オブジェクトが への強い参照を格納している場合のみsomeObject
)。この一時保持サイクルは、ブロックの実行が終了して割り当てが解除されるとなくなります。
実行中、 への参照、への参照、およびへの参照が再びself
格納されます。ただし、ブロックは永続的にどこにも格納されないため、これは一時的なものにすぎません (実装がそうしない限り、完了ブロックでは頻繁ではありません)。someObject
someObject
block
block
self
[someObject successBlock:failure:]
したがって、最初の例では保持サイクルは問題になりません。
一般に、ブロック内のリテイン サイクルが問題になるのは、オブジェクトがブロックを直接実行するのではなく、ブロックを格納している場合のみです。次に、 が を強く参照し、がをself
強く参照していることを簡単に確認できます。ブロック内からivarにアクセスすると、そのブロック内でへの強い参照が自動的に生成されることに注意してください。block
block
self
self
含まれているオブジェクトがコンテナーを強く参照しないようにするのと同じことは__weak SelfClass *weakSelf = self
、メソッドと ivar の両方にアクセスするために使用されます (プロパティを使用する場合のように、アクセサーを介して ivar にアクセスする場合に適しています)。ブロックの参照self
は弱くなり (それはコピーではなく、弱い参照です)、self
強く参照されなくなったときに割り当てを解除できます。
weakSelf
念のため、格納されているかどうかにかかわらず、すべてのブロック内で常に使用することをお勧めします。Apple がこれをデフォルトの動作にしなかったのはなぜだろうか。これを行うことは、実際には不要であっても、ブロック コードに害を及ぼすことはありません。
__block
Objective-C はそのようなオブジェクトの不変性を強制しないため、オブジェクトを指す変数ではめったに使用されません。
オブジェクトへのポインターがある場合は、そのメソッドを呼び出すことができ、これらのメソッドは、 の有無にかかわらずオブジェクトを変更でき__block
ます。__block
基本型 (int、float など) の変数でより (のみ?) 役立ちます。オブジェクトポインタ変数で使用するとどうなるかについては、こちらを参照してください。Apple によるBlocks Programming Topicsで__block
詳細を読むこともできます。__block
編集:__block
オブジェクト ポインターの使用に関する間違いを修正しました。それを指摘してくれた@KevinDiTragliaに感謝します。