ARCについて知っておくべきこと

Objective-Cの自動参照カウント(ARC)は、2011年にiOS 4.0以降、Mac OS X 10.6以降、xCode 4.2以降を使用してAppleによって導入されました。 また、今日ではますます多くのライブラリ、フレームワーク、プロジェクトがARCを使用していますが、これまでのところプログラマーの間では、このテクノロジーが拒否されているか、理解が不完全です。 保持/リリースに慣れている経験豊富なプログラマーは、コンパイラーが行うよりもリンクをカウントするのが優れていて効率的であると考えることがあり、ARCをすぐにクリーンアップする初心者は、メモリー管理についてまったく考える必要がないと思います。すべてが魔法のように自己になります。



ARCはガベージコレクターではありません



ガベージコレクタとは異なり、ARCは使用済みオブジェクトからメモリを自動的に解放せず、バックグラウンドプロセスを開始しません。 彼がしていることは、アプリケーションを構築するとき、プログラマーのためにコンパイルされたコードに分析し、 保持/解放することだけです。

retain、release、retainCount、autoreleaseを直接呼び出すことはできません

deallocをオーバーライドできますが、 [super dealloc]を呼び出す必要はありません。ARCはこれを自動的に行い、必要なすべてのプロパティとインスタンス変数も解放します。 たとえば、通知のサブスクライブを解除したり、サブスクライブしている他のサービスやマネージャーからオブジェクトを削除したり、タイマーを無効にしたり、非Objective-Cオブジェクトを解放したりするために、 deallocを再定義する必要があります。

ARCなしのコード:


- (void)dealloc { [_someIvar release]; [_anotherIvar release]; [[NSNotificationCenter defaultCenter] removeObserver:self]; [super dealloc]; }
      
      





ARCを使用したコード:


 - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; }
      
      





強いリンクと弱いリンク



ARCは、 プロパティの属性を保持して割り当てる代わりに、 強い属性と弱い属性(ローカル変数とivarの__strong__weak )を使用します。 これらは正確に対応するものではありませんが、後で詳しく説明します。 強い属性はデフォルトで使用されるため、指定する必要がないことは明らかです。



 @property AnyClass *strongReference; //(atomic, strong)   @property (nonatomic, weak) id delegate;
      
      





循環参照を回避するために、階層内の「上」にあるオブジェクトを参照する変数とデリゲートのプロパティが弱い必要があります。



ローカル変数はデフォルトで強い( __strong )ので、コード内でこのような構成が可能です:

 NSDate *originalDate = self.lastModificationDate; self.lastModificationDate = [NSDate date]; NSLog(@"Last modification date changed from %@ to %@", originalDate, self.lastModificationDate);
      
      





originalDate変数に格納されたオブジェクトは、ARCがこの変数が使用されている最後の行を見つけるまで生き続け、その後すぐにそれを解放します( releaseに置き換えます)。



弱いリンクを作成するには、 __ weak属性を使用します。

 NSDate * __weak originalDate = self.lastModificationDate; self.lastModificationDate = [NSDate date];
      
      





この例では、このオブジェクトへの強力な参照がなくなった場合、originalDateは2行目に存在しなくなる可能性があります。



ARCの重要で便利な機能:割り当て解除直後、弱いリンクはゼロになります。つまり、ゼロになります。



nilはObjective-Cのすべてのメッセージを受け入れることができるため、 EXC_BAD_ACCESSの問題は過去のものです。 これは、属性の保持/割り当てとの違いです。 オブジェクトを宣言するときも同様のことが起こります:それらは暗黙的にnilで初期化されます。 便利なトリックは、強いローカル変数に弱いプロパティをキャッシュして、オブジェクトが必要な時間だけ生き続けるようにすることです。



 - (void)someMethod { NSObject *cachedObject = self.weakProperty; [cachedObject doSomething]; [cachedObject doSomethingElse]; }
      
      





同じ理由で、デリゲートなどのnilの弱いプロパティをチェックする場合、それらをキャッシュする必要があります。

 id cachedObject = self.delegate; if (cachedObject) { [self.delegate doSomeTask]; } cachedObject = nil;
      
      





キャッシュされたオブジェクトにnilを割り当てると、強力なリンクが削除され、他の強力なリンクがない場合は、オブジェクトの割り当てが解除されます。



まれに、破壊後のオブジェクトへの参照がリセットされないことが必要です。 これにはunsafe_unretained属性があります:

 @property (unsafe_unretained) NSObject *someProperty; NSObject *__unsafe_unretained someReference;
      
      







オートリリース



静的メソッド(例: [NSData data ...]、[NSArray array ...]など)およびリテラル( @ "string"、@ 42、@ []、@ {} )を使用して作成されたオブジェクトもう著作権がありません。 そのようなオブジェクトの存続期間は、それらへの強い参照によってのみ設定されます。

ドキュメンテーションによれば、結果をパラメーターに渡す必要がある場合に、ダブルポインター(* id)に推奨される__autoreleasing属性があります。



 NSError *__autoreleasing error; BOOL ok = [database save:&error]; if (!ok) { //  }
      
      





次に、saveメソッドには次の署名が必要です。

 - (BOOL)save:(NSError * __autoreleasing *) error;
      
      





これにより、メソッド内で作成されたオブジェクトの安全性が確保されます。 この属性なしで変数を宣言すると、コンパイラーは一時的な許可可能な変数を作成し、それをメソッドに渡します。



 NSError * error; NSError *__autoreleasing tmp; BOOL ok = [database save:&tmp]; error = tmp;
      
      







NSAutoreleasePoolは直接使用できなくなりました。 代わりに、 @ autoreleasepool {}ディレクティブを使用することをお勧めします。 このようなブロックに入ると、プールの状態は保持され、終了すると復元され、内部で作成されたすべてのオブジェクトが解放されます。 多数の一時オブジェクトが作成される場合は、ループ内で@autoreleasepoolを使用することをお勧めします。



 for (id object in hugeArray) { @autoreleasepool { //   } }
      
      







ブロック



ブロックはまだコピーする必要があります。



 //property @property (nonatomic, copy) SomeBlockType someBlock; //  someBlockType someBlock = ^{NSLog(@"hi");}; [someArray addObject:[someBlock copy]];
      
      





コンパイラは、循環参照について警告します。



警告:このブロックで「自己」を強くキャプチャすると、保持サイクルにつながる可能性があります

[-Warc-retain-cycles、4]

 SomeBlockType someBlock = ^{ [self someMethod]; };
      
      





ブロックがコピーされるときに、 インスタンスvarisblesを使用している場合、ブロックも自己をキャプチャします



そのような場合を回避するには、 selfへの弱いリンクを作成する必要があります。

 __weak SomeObjectClass *weakSelf = self; SomeBlockType someBlock = ^{ SomeObjectClass *strongSelf = weakSelf; if (strongSelf) { [strongSelf someMethod]; } };
      
      





__weak属性を使用して外部で宣言されたブロック内のオブジェクトを処理する必要があります。



橋の建設



CoreFondationオブジェクトはどうですか? 彼らにとって、リンクの手動カウントはキャンセルされていません。 現在、直接キャストは機能しません。これにはいくつかの特別なキーワードがあります。



 id my_id; CFStringRef my_cfref; NSString *a = (__bridge NSString*)my_cfref; CFStringRef b = (__bridge CFStringRef)my_id; NSString *c = (__bridge_transfer NSString*)my_cfref; // -1    CFRef CFStringRef d = (__bridge_retained CFStringRef)my_id; //  CFRef +1
      
      











おわりに



ARCを使用します。 それはより簡単で安全であり、あなたの時間と神経を節約します。



参照資料


ARCに関するClangドキュメントセクション

ARCパフォーマンスの記事



All Articles