Xcode4.2から採用されたARC(Automatic Reference Counting)ですが、
ちょっと「おっ!」と思ったところがあったので簡単なテストをしてみました。
まずは、簡単なプロジェクトを作成します。
Xcode4.2で言うところのEmpty Applicationでプロジェクトを作成します。
とりあえずは、Use Automatic Reference Countingのチェックを外しておきます。
iOS4の挙動も試したいので、iOS Deployment Targetは、4.2としておきます。
今回は、超簡単なナビゲーションベースのアプリを作りますので、
RootViewControllerというビューコントローラを一つだけ作成します。
xibファイルも一緒に。
ビューコントローラの中身は特に何も配置しませんが、寂しいので背景だけ設定しておきます。
AppDelegateのdidFinishLaunchingWithOptionsでRootViewControllerとUINavigationControllerを追加します。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
//
RootViewController* root = [[RootViewController alloc] initWithNibName:@"RootViewController" bundle:nil];
UINavigationController* navi = [[UINavigationController alloc] initWithRootViewController:root];
[root release];
[self.window addSubview:navi.view];
//
[self.window makeKeyAndVisible];
return YES;
}
これ、UINavigationControllerをスタック変数に代入していますので、後々ポインタが行方不明になる悪いコードですが、そこは目をつぶってくださいね。
それから、RootViewController.mに次のように記述して、ViewDidLoadとその1秒後にログを出力させます。ViewDidLoadはAppDelegateのdidFinishLaunchingWithOptionsと同じRunLoop内で処理されているかもしれませんので、そこを抜けて、その後のRunLoopから呼び出されるように1秒後にも表示させるようにしています。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"ViewDidLoad: %@", self.navigationController);
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(onTimer:) userInfo:nil repeats:NO];
}
- (void)onTimer:(NSTimer *)timer {
NSLog(@"onTimer: %@", self.navigationController);
}
これで、iPhone 5.0 Simulator、iPhone 4.3 Simulator、どちらで実行させても普通にログを出力します。
2011-11-07 16:30:45.435 ARCtest[21675:10103] ViewDidLoad: <UINavigationController: 0x6a5e0d0>
2011-11-07 16:30:46.438 ARCtest[21675:10103] onTimer: <UINavigationController: 0x6a5e0d0>
次に、いよいよARCを有効にしてみます。
で、先ほどのコードでreleaseを使っていたので、少しだけ変更します。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
//
RootViewController* root = [[RootViewController alloc] initWithNibName:@"RootViewController" bundle:nil];
UINavigationController* navi = [[UINavigationController alloc] initWithRootViewController:root];
[self.window addSubview:navi.view];
//
[self.window makeKeyAndVisible];
return YES;
}
iPhone 5.0 Simulatorで実行した場合のログは次のようになります。
2011-11-07 16:49:12.542 ARCtest[21848:10103] ViewDidLoad: <UINavigationController: 0x6c94e30>
2011-11-07 16:49:13.544 ARCtest[21848:10103] onTimer: (null)
これは予想通り。こうなるような気がしてました。
そして、iPhone 4.3 Simulatorで実行した場合には、そもそもRootViewController自体が表示されません。
iOS4とiOS5では、ARCで自動リリースするタイミングが違うのか、実際にビューを表示するタイミングが異なっているということなのでしょうか。
まぁ、あんまり深く掘り下げるつもりはありませんので、この辺で止めときます。
ARCでの正しい記述は次の通りです。
@implementation AppDelegate {
UINavigationController* navi;
}
としてクラスのプロパティとして宣言しておきます。
AppDelegateのdidFinishLaunchingWithOptionsは、ちょっとだけ変更されて下のようになります。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
//
RootViewController* root = [[RootViewController alloc] initWithNibName:@"RootViewController" bundle:nil];
navi = [[UINavigationController alloc] initWithRootViewController:root];
[self.window addSubview:navi.view];
//
[self.window makeKeyAndVisible];
return YES;
}