Pages

2011年11月7日月曜日

Xcode 4.2 ARCの挙動を少し調べてみました

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;
}

0 コメント:

コメントを投稿