__weak MyViewController *__weakSelf = self; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse* response, NSData* data, NSError* error){ __weakSelf.label.text = [[NSString alloc] initWithData:data encoding:NSUTF8Encoding]; }];このようなコードを安全に実行するためにはselfを一度__weakな変数に代入して、それをBlocksにキャプチャさせるようにしないと、以下のような理由で安全ではなくなってしまいます。
- __strongを使うと、対象の変数をキャプチャしているBlocksの実行が終わるまで対象の変数がreleaseされなくなるばかりか、最悪の場合は循環参照が発生してメモリが絶対に開放されなくなってしまいます。
- __unsafe_unretainedを使うと、Blocksの実行が終わるまでの間に対象の変数がreleaseされてしまうとEXC_BAD_ACCESSでクラッシュします。
そこで便利に使えるのが
__has_feature(objc_arc_weak)
と__has_feature(objc_arc)
マクロです。こいつらを使うと簡単に現在のビルド環境・ターゲット環境がARCを導入しているか、weakは使用可能か、を判断できます。たとえば私は以下の様なマクロを組んで、// ARC & memory management // Use these prefixes to be compatible with ARC on iOS 5/ ARC on iOS 4.X / non-ARC // #if __has_feature(objc_arc_weak) // iOS 5 or above #define __my_block_weak __weak #define __my_block_weak_unsafe __weak #elif __has_feature(objc_arc) // iOS 4.X #define __my_block_weak __strong #define __my_block_weak_unsafe __unsafe_unretained #else // iOS 3.X or non-ARC projects #define __my_block_weak __strong #define __my_block_weak_unsafe __block #endifこんな感じのコードにしてます。
__my_block_weak MyViewController *__weakSelf = self; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse* response, NSData* data, NSError* error){ __weakSelf.label.text = [[NSString alloc] initWithData:data encoding:NSUTF8Encoding]; }];こうするとどの環境でも(比較的)安全にblocks内部でselfを触ることができるという寸法です。
ここで気になるのが
__has_feature(objc_arc_weak)
の判定条件です。個人的にこいつはiOS SDK 5.0以上を使っていたらターゲット環境がiOS 4とかiOS 3とかでも無条件で1を返してしまって使えないんじゃないのかと危惧していたのですが、なんとIPHONEOS_DEPLOYMENT_TARGET
の値を見てきちんと値が変化する仕組みになっています!なので例えばSDKは最新だけれどiOS 3もサポートしたいみたいなプロジェクトでは__has_feature(objc_arc)
の値が自動的に0になって良い感じに分岐してくれるというわけです。安心して使いまくりましょう!