【Objective-C】init内でself.viewを参照してはいけない

Objective-c

概要

init内でインスタンス生成時に一度だけやりたいことを書く場面は多いと思います。
しかし、このinit内でselfを参照しておかしい挙動になって困っていないですか?
この記事では、init内でselfを参照することによって起きてしまう問題点
とどう解決するかなどを解説しています。

initメソッド内でself.viewを参照すべきではない

結論から先に述べますと、viewのライフサイクルが変わってしまい、通常とは違う動作を行います。

じゃぁ、どうすれば良いのか?

init内では、インスタンスの生成にとどめましょう。
frameに関わる変更はloadViewやviewDidLoad以降のメソッドで行う様にする。

これだけです。

それでは、詳細を見て行きましょう。

通常の動作の確認。

まず、通常のライフサイクルを簡単に確認しましょう。

- (instancetype) init {
    self = [super init];
    if(self) {
        NSLog(@"init");
        UILabel *label = [[[UILabel alloc] init] autorelease];
        // labelの初期化
        NSLog(@"label instantiated!");
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"viewDidLoad");
}

- (void) loadView {
    [super loadView];
    NSLog(@"loadView");
}

これの実行結果は以下です。

init
label instantiated!
loadView
viewDidLoad

予想通りの結果です。

では、init内でself.viewを参照するとどうなるか。

init内を以下の様に変更します。
(loadView、viewDidLoadは変更しません。)

- (instancetype) init {
    self = [super init];
    if(self) {
        NSLog(@"init");
        UILabel *label = [[[UILabel alloc] initWithFrame:self.view.frame] autorelease];
        // labelの初期化
        NSLog(@"label instantiated!");
    }
    return self;
}

結果はこちらです。

init
loadView
viewDidLoad

label instantiated!

先にloadView及びviewDidLoadが走ってしまう事が確認出来ます。

どう言う場合に悲惨な事が起こるか

例えば、このUIViewControllerは生成後に何らかのUINavigationControllerのインスタンスにpushViewController:animatedされるとしましょう。

TestViewController *viewController = [[[TestViewController alloc] init] autorelease];
[navigationController pushViewController:viewController animated:YES];

の様な形ですね。

そして、このTestViewControllerのインスタンスのviewDidLoadでself.navigationControllerを参照したいとしましょう。

通常時

先ほどの正しく動くソースのviewDidLoadを以下の様に変更します。

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"viewDidLoad");
    NSLog(@"self.navigationController %@", self.navigationController);
}

self.navigationControlerを新たに参照する様に追加しています。

実行結果は以下です。

init
label instantiated!
loadView
viewDidLoad
self.navigationController TestNavigationController: 0x75ad3a0

正しくUINavigationControllerのインスタンスが参照出来ています。

init内でself.viewを参照してしまうソースコード

問題のソースコードのviewDidLoadを同じ様に変更した実行結果です。

init
loadView
viewDidLoad
self.navigationController (null)
label instantiated!

navigationControllerが参照出来ていません。

それもそのはず。

initメソッド内でTestViewControllerのviewDidLoadが参照されてしまっているため、まだこのインスタンスはUINavigationControllerのスタックに積まれていないためです。

と言う事で、init内ではself.viewは参照しない様にするのが懸命でしょう。

誰かのお役に立てば。

コメント

タイトルとURLをコピーしました