IOSのblockとretain cycle(クラシック)

4912 ワード

IOSのblockとretain cycle(クラシック)
retain cycleの生成
retain cycleといえば、まずObjective-Cのメモリ管理メカニズムについてお話しします.
C言語のスーパーセットとして、Objective-CはC言語でメモリを手動で管理する方式を継続しているが、C++の極めて非人道的なメモリ管理とは異なり、Objective-Cはメモリ管理の難しさを減らすメカニズムを提案している.例:メモリ数.
Objective-Cでは、NSObjectから継承されたクラスには、retainとreleaseの2つの方法があります.オブジェクトのretainを呼び出すと、そのオブジェクトのメモリカウントに1が加算されます.逆に、releaseを呼び出すと、オブジェクトのメモリカウントが1減少し、オブジェクトのメモリカウントが0の場合にのみ、このオブジェクトが本当に解放されます.この場合、オブジェクトのdellocメソッドはメモリ回収前の作業に呼び出されます.
メモリカウントメカニズムの利点は、使用権を明確に割り当てることです.例えば、あるオブジェクトAが別のオブジェクトBを使用しようとすると、AはAがBを使用することを示すために一度Bをretainし、Bが使用されると、AはBのreleaseメソッドを呼び出して使用権を放棄する.これにより、1つのオブジェクトを複数の他のオブジェクトで使用できます.これを使用するオブジェクトとして、自分以外で使用されるオブジェクトの使用状況(メモリ面)に関心を持つ必要はありません.一般的に、クラスのメンバー変数では、retainとreleaseはそれぞれ付与と自分自身の解放時に発生します.これがObj-Cプログラムの古典的な書き方です.
ヘッダファイル:
@property (nonatomic,retain) NSObject *obj;

はい.mファイル:
- (void)dealloc{
    [obj release];
    [super dealloc];
}

OK、この方法でメモリを簡単に管理できますが、retain cycleという問題があります.
Retain cycle、中国語に訳すと保留環と呼ぶでしょう.親が子を持ち、子が親の解放に伴って解放される場合、2つのオブジェクトが互いに親である場合はどうしますか?
例えばAとBの2つのオブジェクト、AはBを持っていて、Bは同時にAを持っていて、上述の規則に従って、AはBが解放された後にやっと解放することができて、同様にBはAが解放された後にやっと解放することができて、双方がすべて相手の解放を待っている時、retain cycleは形成して、結果は、2つのオブジェクトはすべて永遠に解放されないで、最終的にメモリは漏らします.
retain cycleは、プログラミング中にいくつかの問題に注意しなければなりません.たとえば、子オブジェクトが親オブジェクトを参照するときに弱い参照、すなわちassignを使用するか、たとえば
@property (nonatomic,assign) NSObject *parent;

retain cycleの変数の1つをnilに設定し、ループbreakを削除します.注意すれば、これは特に大きな問題ではありません.
はい、注意点は確かに問題ではありませんが、IOS 4.0匹の後、blockの出現は、もっと慎重にする必要があります.
blockとメモリ管理
blockは柔軟に使用できるコードで、変数として渡したり、値を付けたり、関数体に宣言したりすることができます.もっと柔軟なのは、外部の環境を参照することができます.最後に、blockが外部環境を参照できる以上、blockが呼び出されたときの環境変数が解放されないことをどのように保証するかを考えなければなりません.(block呼び出しのタイミングは勝手かもしれません)
答えは、blockに参照された変数が自動的にretainされることです.そうすれば、少なくとも私たちの呼び出しが有効であることを保証することができます.
ここまで言ったら何か考えられますか.はい、やはりretain cycleです.blockのretainは暗黙的なので、retain cycleの問題が発生しやすい.
block自体もオブジェクトと見なすことができ、ライフサイクルも存在し、持つこともできるので、このような状況が発生した場合、私たちは注意しなければなりません.例えば、
DoSomethingManager *manager = [[DoSomethingManager alloc] init];
manager.complete = ^{
    //...complete actions
    [manager otherAction];
    [manager release];
};

retain cycleはこのように形成され、releaseを呼び出してもmanagerは解放されません.managerとblockは互いに持っているからです.retain cycleを解除するには、次のように書くことができます.
DoSomethingManager *manager = [[DoSomethingManager alloc] init];
manager.complete = ^{
    //...complete actions
    [manager otherAction];
    manager.complete = nil;
    [manager release];
};

managerのcompleteはnilに設定されています.そうするとretain cycleも破壊されます.ブロックを再びコールバックする必要がないことを前提にしています.
ここまで書いておけばそれまでだが、新世紀には常に新たな挑戦があり、Appleに新たな技術ARCが登場したことにある.
ARCとretain cycle
ARC(Auto Reference Counting)は、自動参照カウントに翻訳され、Appleがメモリ管理をさらに簡素化するために発売した技術です.自動メモリ管理のために生まれたが、本格的な自動管理とは言えない.これはARCがコンパイル期間の技術であり、コードを自動的に認識してretain/releaseの形式に変換するためであり、この面ではARCはコードの書き方を簡略化し、一部の性能の最適化を提供しているにすぎない.Javaのような言語でゴミを完全に回収できるわけではありません(基本的には).ARCの詳細については、次のURLを参照してください.
http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
以下、主にARCのretain cycleについてお話しします.
ARCでは、変数は3つのキーワードで修飾できます.
__strong:この変数に割り当てられたオブジェクトは自動的にretainされ、blockで参照されるとblockも一度retainされます._unsafe_unretained:この変数に値を付与してもretainされない、つまり彼に修飾された変数の存在はオブジェクトの信頼性を保証することができず、解放された可能性があり、安全ではないポインタが残っている.block retainにはされません. __week:類似_unsafe_unretainedは、持つオブジェクトが解放されると変数が自動的にnilに設定されるだけで、より安全ですがIOS 5のみです.0以上のシステムサポートは、block retainにもサポートされません.
また__も使えますblockキーワードは変数を修飾し、この変数がblockで変更できることを示します(オブジェクトの属性を変更するのではなく、値を変更します.ポインタの指向を変更すると理解できます).自動retainされます.
他の変数と異なるのは被_block修飾変数はブロックに変数のアドレスを保存します.(その他は変数の値)
まず、上のコードをこのように書くことができます.
DoSomethingManager *manager = [[DoSomethingManager alloc] init];
manager.complete = ^{
    //...complete actions
    [manager otherAction];
    manager.complete = nil;
};

大丈夫です.ただARCで禁止されているreleaseを外しただけです.
もちろん、私たちもそう書くことができます.
__block DoSomethingManager *manager = [[DoSomethingManager alloc] init];
manager.complete = ^{
    //...complete actions
    [manager otherAction];
    manager = nil;
};

ARCを使わなければ、managerはblockでretainされることはありませんが、ARCを採用すると少し複雑になります.blockはretain manager変数を返しますが、_block変数はより下位の変数アドレスを保存するため、この変数が他のオブジェクトに指さされると、blockは元のオブジェクトに責任を負わず、結果として前のオブジェクトがreleaseされ、retain cycleが破壊される.
あるいはこう書きます.
__block DoSomethingManager *manager = [[DoSomethingManager alloc] init];
DoSomethingManager __week *weekmanager = manager;
manager.complete = ^{
    //...complete actions
    [weekmanager otherAction];
};

上の_weekも使えます_unsafe_unretained代替ですが、_WeekはIOS 5をサポートしていませんが、より安全です.0以下のシステム.
被_weekまたは_unsafe_unretained修飾変数はblock retainにはならないのでretain cycleは形成されませんが、注意して、あなたのオブジェクトがcompleteの前に解放されないことを保証します.そうしないと、あなたの意図しない結果が得られます.