Objective-C
iOS
ARC
メモリ管理

[Objective-C] ARC有効時のコンパイラの挙動

More than 3 years have passed since last update.

エキスパート Objective-C プログラミング書籍の中でARC有効時にコンパイラがどういうコードを想定するか、擬似コードで紹介してくれています。

ARC有効時の各種修飾子がどうなるのか、見ておくと変なところではまらずに済むと思います。
いくつか大事な部分を抜粋、紹介させてもらいます。

__strong修飾子

alloc / new / copy / mutableCopy系

{
    id __strong obj = [[NSObject alloc] init];
}

擬似コード

// コンパイラによる擬似コード
id obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(obj, @selector(init));
objc_release(obj);

スコープを抜けた時点で自動的にreleaseされるようです。自動変数と同じように使える感じですね。

書籍には明示されていないんですが、これらはおそらく文法によってARCが自動的に擬似コードに相当するものを挿入するようにできているんだと思います。(なので、メソッド名がallocやallocHogeなどではなく、allocateだと意味が違ってくる)

→コメントで教えて頂きました。Clang のドキュメントにしっかりと記載されているみたいです。

オブジェクト生成メソッド系

{
    id __strong obj = [NSMutableArray array];
}

擬似コード

// コンパイラによる擬似コード
id obj = objc_msgSend(NSMutableArray, @selector(array));
objc_retainAutoreleaseReturnValue(obj);
objc_release(obj);

こちらの例ではobjc_retainAutoreleaseReturnValue関数が呼ばれます。
これは、retainしつつ、autoreleasepoolに登録されたオブジェクトを返す関数です。

__weak修飾子

{
    id __weak obj1 = obj;
}

擬似コード

// コンパイラによる擬似コード
id obj1;
objc_initWeak(&obj1, obj);
objc_destroyWeak(&obj1);

objc_initWeak関数で初期化し、スコープ終了とともにobjc_destroyWeak関数で破棄します。
objc_initWeakは以下を実行するようです。

obj1 = 0;
objc_storeWeak(&obj1, obj);

objc_destroyWeak関数は、上記を0を引数に呼び出します。

objc_storeWeak(&obj1, 0);

これは、weakテーブルと呼ばれる、参照カウントテーブルと同様に、ハッシュテーブルになっていて、__weak修飾子付き変数を管理しています。
objc_storeWeak関数によって、登録および解除を行うようです。

__weak修飾子付き変数に直接インスタンスを生成すると?

例えば以下のコード。

{
    id __weak obj = [[NSObject alloc] init];
}

これは以下のように解釈されます。

// コンパイラによる擬似コード
id obj;
id tmp = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(tmp, @selector(init));
objc_initWeak(&obj, tmp);
objc_releae(tmp);
objc_destroyWeak(obj);

色々やっていますが、即座に解放されてしまっているのが分かります。

感想

ARC以外にも、Blocksがどういう仕組みで動くのか、こちらも擬似コードを使って変数がキャプチャされる様子を詳細に解説してくれます。
ARC、Blocks、GCDについてとても分かりやすく解説してくれているのでおすすめの一冊です。
特に、自分のようにARCしか触れたことない人にとっては、内部でどういうことがされているかを把握しておくのはデバッグにも役立つと思います。