HOME » Natsu note » iOS » [iOS5] ARC : Autorelease, キャスト, 環境設定

[iOS5] ARC : Autorelease, キャスト, 環境設定 2011/11/30/|iOS, ,

これまでの記事はこちら:

ARCまとめの最終回はAutoreleaseとキャストについてです。また、最後で簡単にですが、Xcodeの環境設定についても触れます。

Autorelease

ARC環境下では、これまでのNSAutoreleasePoolは使えません。そうは言っても、別にAutorelease環境がなくなってしまったわけではなく、作法が少し変わったのですね。

まずは、参考までにmain.mを見てみましょう。

非ARC(マニュアルメモリ管理)

int main(int argc, char *argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

ARC

int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

ARC環境では、NSAutoreleasePoolを作成する代わりに、@autoreleasepoolブロックが使用されています。

@autoreleasepoolブロックの始まりが、NSAutoreleasePoolオブジェクトの生成で、ブロックの終了が、NSAutoreleasePoolオブジェクトの解放だと思えばよいでしょう。

@autoreleaseを使うと、ブロック内で生成されたautoreleaseオブジェクトは、すべてブロックの終了とともに解放されます。

なお、@autoreleasepoolブロックは、ARC, 非ARCに関係なく使用できるようです。

Bridged Cast

これまで、(void *)と(id)間のキャスト(CとObjective-C間のキャスト)はそのまま記述できていましたが、ARC環境下では、明示的にオーナーシップの所在を明らかにする必要があります。これは、Objective-CのオブジェクトとCore Foundationのオブジェクト間のキャスト(Toll-Free bridgedが可能なもの)でも同様です。

Toll-Free Bridgedとは

Toll-Free Bridgedとは、NSArrayとCFArrayRef, NSStringとCFStringRefのように、オブジェクト構造が同じためにそのままキャストが可能なものを言います。

※ Toll-Free Bridgedのオブジェクト一覧は、
Core Foundation Design Concepts – Toll-Free Bridged Types
を参照のこと。

例えば、非ARC環境では、Toll-Free Bridgedのオブジェクトは、以下のように単純にキャストすることができました。

NSString *string = [NSString stringWithFormat:...];
CFStringRef cfString = (CFStringRef)string;

Core Foundationの関数にObjective-Cのオブジェクトを引数として渡すときも同様です。

ARC環境ではコンパイルエラー

しかし、上記のようなコードは、ARC環境ではコンパイルエラーとなります。
“Cast of Objective-C pointer type ‘NSString *’ to C pointer type ‘CFStringRef’ (aka ‘const struct __CFString *’) requires a bridged cast”

エラー内容を見てみると、bridged castが必要だと言っています。さらに、コンパイラが親切に修正例も教えてくれています。候補は二つあって、
“Use __bridge to convert directly (no change in ownership)”
“Use __bridge_retained to make an ARC object available as a +1 ‘CFStringRef’ (aka ‘const struct __CFString *’)”
です。

オーナーシップ権を変更しないのなら、__bridgeを使い、CFStringRefの方でretainカウントを+1したければ、__bridge_retainedを使いなさいということですね。

Objective-CオブジェクトがARCの対象であるのに対し、Core FoundationオブジェクトはARCの対象ではありません。したがって、これらの間でキャストをする際には、コンパイラにオーナーシップ権をどうすべきか指示する必要があるというわけです。

3つのbridgeタイプ

Bridgeタイプには上述した__bridge, __bridge_retainedの他にもう一つ、__bridge_transferというものがあります。

__bridge

NSString *string = [NSString stringWithFormat:...];
CFStringRef cfString = (__bridge CFStringRef)string;

単純なキャストを行うときに使用します。オーナーシップ権は移行しませんので、キャスト元の変数(上の例ではstring)が破棄された(スコープ外に出るなど)あとでの、キャスト先の変数(cfString)の使用は非常に危険です。

__bridge_retained

NSString *string = [NSString stringWithFormat:...];
CFStringRef cfString = (__bridge_retained CFStringRef)string;
...
CFRelease(cfString); // Core FoundationはARC対象外なので自分でreleaseする。

キャストと同時に、キャスト先の変数(cfString)のretain countが+1されます。これにより、キャスト元の変数(string)が破棄されたあとでも、安全にキャスト先の変数を使用することができます。ただし、上記のようにObjective-CからCore Foundationにキャストした場合、Core FoundationがARC対象外なので、最終的に自分でcfStringをreleaseして上げる必要があります。


__bridge_retainedキャストを用いる代わりに、CFBridgingRetain()関数を利用することもできます。

NSString *string = [NSString stringWithFormat:...];
CFStringRef cfString = CFBridgingRetain(string);
...
CFRelease(cfString); // Core FoundationはARC対象外なので自分でreleaseする。

__bridge_transfer

オーナーシップ権が移行されます。キャストと同時に、キャスト元の変数はreleaseされ、キャスト先の変数がretainされると考えればよいでしょう。__bridge_transferは、Core FoundationオブジェクトをObjective-Cオブジェクトにキャストするときによく利用されると思います。

CFStringRef cfString = CFStringCreate...();
NSString *string = (__bridge_transfer NSString *)cfString;

// CFRelease(cfString); __bridge_transferを使ったのでreleaseは不要!!

__bridge_transferキャストを用いる代わりに、CFBridgingRelease()関数を利用することもできます。このコードだと、CFReleaseが不要な理由が明らかでしょう。

CFStringRef cfString = CFStringCreate...();
NSString *string = CFBridgingRelease(cfString);

キャストのタイプを決めるポイント

キャストのタイプを決めるときは、

  • キャスト元、先のどちらがARC対象でどちらがARC非対象か
  • ARC対象外のオブジェクトのreleaseは誰がすべきか
  • それぞれのオブジェクトの生存範囲はどこか(ARCの場合スコープ外に出れば破棄される)

あたりをポイントに考えると分かりやすいと思います。

Xcodeの環境設定

最後になりましたが、簡単にXcodeの環境設定についてです。

非ARCの旧プロジェクトをARCに対応させたい場合、Xcodeのメニューから”Edit”->”Refactor”->”Convert to Objective-C ARC…”を選択すればOKです。最初にコンパイラがプレチェックをして、問題なければ移行できます。

なお、このとき、ARCに変換したいコードを選択可能ですので、非ARCのまま残しておきたいコードは対象コードのチェックを外しましょう。

また、プロジェクトのターゲット設定で、”Build Phases”->”Compile Sources”の”Compiler Flags”に”-fno-objc-arc”が追加されているコードはARCの対象外となります。外部ライブラリを使用している場合など、自分ではコントロールしきれないものはARCの対象から外しておくと便利だと思います。

ちなみに、Refactorのプレチェックは、まだまだおかしなエラーも出ることがあるようです。何度もやると結果が違うので、このあたりはまだ試行錯誤が必要かもしれません。

プレチェック時に出るエラーに関しては、以下のチュートリアルの最後にあるMigration Woesが参考になります。
Beginning ARC in iOS 5 Tutorial Part 1 | Ray Wenderlich

まとめ

少し駆け足になりましたが、Autorelease、キャストと、Xcodeの環境設定についてまとめました。これで、以前の記事も合わせると、ARCに関しては大まかですが一通り理解できたことになります。

最初は少し取っ付きにくい部分がありますが、慣れてしまえばかなり使いやすいと思います。何よりもコード量が減るのがありがたいですね。

ARCを使うことで、不正アクセスによるクラッシュなどはずいぶん減らせるのではないでしょうか。ただし、循環参照やキャストに気をつけないとメモリリークは減りません。むしろ増えてしまうかもしれないので注意しましょう。

質問、間違いの指摘などはツイッターでお願いします。@natsun_happy

参考資料

ARCの詳細を書きました↓

iOS5プログラミングブック
加藤 寛人 吉田 悠一 藤川 宏之 西方 夏子 関川 雄介 高丘 知央
インプレスジャパン
売り上げランキング: 243,952

iOS 6 UICollectionViewについて書きました↓

iPhoneアプリ開発エキスパートガイド iOS 6対応
加藤 寛人 藤川 宏之 高丘 知央 西方 夏子 吉田 悠一 関川 雄介
インプレスジャパン
売り上げランキング: 4,856