ARC 変数の制御レベル - Automatic Reference Counting
SPECIAL
変数の制御レベル
ARC (Automatic Reference Counting) では、変数宣言の際に、その変数の制御レベルを指定できるようになっています。
これによって、retain, release, assign, autorelease を直接制御できなくても、それぞれに似たような制御をコンパイラにしてもらうことができるようになっていました。
ARC で指定できる制御レベルには、次の 4 つがあるそうです。
1. __strong
普通に宣言された変数は、この制御レベルになるようです。
この変数に値を代入する際は、まず左辺値が retain されて変数に格納された後、その変数に格納されていた以前の値が release される流れになるようです。
この操作は atomic になっていないらしく、マルチスレッド環境では @synthesize 等で適切に制御する必要があるとのことでした。
2. __weak
この制御レベルで定義された変数にオブジェクトを代入すると、その値は retain されずに、既に代入されていた値と置き換えられる感じになるようです。
代入後、変数が指すオブジェクトは代入したオブジェクトと同じ値となっていて、そして、代入されているオブジェクトが他の変数に格納されていたとしても、それが解放された時には、この変数の値も nil になってくれるようでした。
代入したオブジェクトは retain されないので、お互いにお互いのポインタを保持した場合でも、お互いがにらめっこして dealloc が呼ばれなくなるようなこともないですし、この制御レベルで保持しているオブジェクトがどこか別の場所で解放された場合には nil になるので、いつの間にかオブジェクトがなくなっていたときに、それがなくなったものとして処理することが可能なため、デリゲートの呼び出し先を保持するような用途に適切なように思います。
なお、この __weak 権限の変数は、参照時にはそれが retain & autorelease されるようでした。
そのため、__week 権限の変数を参照すると、つまりその参照先であるオブジェクトが retain されてオートリリースプールに入れられるので、その分だけオブジェクトの生存期間が延びることになるようです。もっとも、参照している間に別の場所でリリースされても困るので、使っている間は生存が確保されるというのは、何かと都合が良さそうです。
使い終わったら早めに解放したい場合には、使う範囲を @autoreleasepool ブロックで括ってあげれば大丈夫です。
ちなみに __week 権限の変数に autorelease されていないオブジェクト(alloc & init で生成したものを直接や、"__attribute__((ns_returns_retained))" として定義されたメソッドの戻り値など)の値を代入すると、コンパイラも警告してくれましたけど、そのオブジェクトは構築後に、すぐに release される様子です。
このときも、代入したオブジェクトが解放されるので、その直後にその変数の値を参照すると nil が取得されます。
3. __unsafe_unretained
この制御レベルが指定されると、従来通りのポインター的な変数に似た動きになるようです。
この変数にオブジェクトを代入しても retain されることもなく、代入した変数が別の場所で release されても nil になることもなく、従来通りな感じです。
もちろん、解放されたオブジェクトのアドレスがそのまま残ってしまう場合があるため、それを使用してしまえば、"EXC_BAD_ACCESS" "message sent to deallocated instance" という例外エラーになってしまいます。
このような感じで、基本的には従前のポインタのような感じですけど、ただしまったく ARC が関与しないわけではないようでした。
この制御レベルを付けて宣言した変数に、オブジェクトを alloc & init で代入した場合、コンパイラも警告してくれますが、代入された変数は、直ちに release されるようでした。
従来のポインタであれば、当然のようにオブジェクトを保持してくれますけど、この制御レベルの変数には、あくまでも __strong で確保されているオブジェクトを格納する感じになるようです。
4. __autoreleasing
この制御レベルを指定した場合、代入する値は retain & autorelease されて、変数に格納されるようです。
新たにオブジェクトを上書きで代入しても、前に格納されているオブジェクトは autorelease されているので、そのまま支障なく、値を差し替えることができる感じのようです。
ちょうど autorelease を定義するのと同じような感じなのかもしれないですね。
ちなみにこの制御レベルは、大域変数には指定できないようでした。
このような感じで、これまではプログラム制御の中で行っていた変数の制御を、変数宣言のところで行う感じになりました。
変数宣言で制御レベルを設定してしまえば、後のプログラムでは基本的に、同じように扱えばそれで済むようになっているのが、とても画期的なことのように思います。
その変数がどのように制御されるかを宣言を見れば把握できますし、その制御方法を変更したい場合にも、制御レベルを書き換えることで大よそ対応できてしまうところがとても魅力的に思えました。