ARC-enabled なライブラリを Snow Leopard に対応させるために、 weak 修飾子を unsafe_unretained 修飾子に書き換たいことがまれにある。基本的には単純に書き換えるだけでいいが、注意すべき点が2つある:
-
unsafe_unretained
プロパティが参照するオブジェクトが解放されたらプロパティにnil
をセットする -
__unsafe_unretained
変数が参照するオブジェクトを使う前に強参照を作る
1. プロパティについて
weak
プロパティはオブジェクトが解放されると同時に nil
がセットされるが、 unsafe_unretained
プロパティは無効な領域を参照し続ける:
@property(weak) id weakProp;
@property(unsafe_unretained) id unsafeUnretainedProp;
// ...
- (void)propExample {
id obj = [[NSObject alloc] init];
self.weakProp = obj;
self.unsafeUnretainedProp = obj;
obj = nil; // ここでオブジェクトが解放される
// ここでは self.weakProp == nil
// ここでは self.unsafeUnretainedProp == 無効なアドレス
}
weak
プロパティを unsafe_unretained
プロパティに書き換えた場合、参照しているオブジェクトが解放されたタイミングで nil
をセットしてやる必要がある。
上に掲載した例は同一スコープ内でオブジェクトを作ってすぐ解放する単純なコードなので解放タイミングが掴みやすいが、実際の参照関係はより複雑になる場合がほとんどである:
- (void)complexRefExample {
self.unsafeUnretainedProp = [self.mutableDict objectForKey:@"obj"];
[self.mutableDict removeObjectForKey:@"obj"];
// ここで、
// 強参照していたのが self.mutableDict だけなら:
// self.unsafeUnretainedProp == 無効なアドレス
// 他のオブジェクトがまだ強参照しているなら:
// self.unsafeUnretainedProp == オブジェクトの有効なアドレス
}
オブジェクトが解放されるタイミングが予測できない場合、メモリリークする可能性と引き換えに strong
プロパティを使う手もある。
2. 変数について
__weak
変数も weak
プロパティと同様に、参照するオブジェクトが解放されると同時に nil
がセットされる:
- (void)varExample1 {
id obj = [[NSObject alloc] init];
__weak id weakVar = obj;
__unsafe_unretained id unsafeUnretainedVar = obj;
obj = nil; // ここでオブジェクトが解放される
// ここでは weakVar == nil
// ここでは unsafeUnretainedVar == 無効なアドレス
}
__weak
変数を __unsafe_unretained
変数に書き換えた場合、手動で nil
をセットする代わりに、強参照を作ってオブジェクトを解放させないようにするのが手軽である:
- (void)varExample2 {
{
id obj = [[NSObject alloc] init];
__unsafe_unretained id unsafeUnretainedVar = obj;
id var = unsafeUnretainedVar;
obj = nil; // まだ var が参照しているのでオブジェクトは解放されない
// ここでは unsafeUnretainedVar == obj
}
}