iOS6/7のviewDidLoadが呼ばれるタイミングの違い
「viewDidLoad
が呼ばれるタイミングは、UIViewController
のview
プロパティが初めてアクセスされたタイミングである」と公式ドキュメントに書いてあります。
しかし、 いつ・誰がview
プロパティにアクセスするのか についてはフレームワーク内部なのでブラックボックスとなっています(スタックトレースを追えばすぐ分かるんですけど)。
例えばこういう書き方をしたとします。
HogeViewController *vc = [HogeViewController new];
[self.navigationController pushViewController:vc animated:YES];
vc.hogeLabel.text = @"hoge";
このコードはiOS6ではラベルが書き換わるのだけど、iOS7ではvc.hogeLabel
がnil
と評価されるため、動作しません。ここでは画面遷移をコードで管理していますが、storyboardを用いて、[segue destinationViewController]
で取得した場合でも同じことが言えます。
つまるところ、iOS6ではpushViewController:animated:
やpresentViewController:animated:
が実行された時点でview
プロパティにアクセスされ、viewDidLoad
がコールされていたのに対して、iOS7ではそのスタックを抜けた時点で初めてview
プロパティにアクセスされるよう変更されました。
遷移処理をその場で行わず、メインスレッドにキューイングするように変更されたのでしょうね。
こんなトラップにハマる人はまずいないと思うのですけど、イニシャライザやviewDidLoad
で時間の掛かる処理(ネットワーク通信など)を行い、プログレス表示を行いたい場合、本来は非同期処理にしてコールバックを受け取るような仕組みを作る必要があります。
これを同期的に処理を書こうと考えたのか、
[self showProgress]; // keyWindowに対してプログレス用のviewをaddする
HogeViewController *vc = [HogeViewController new];
[self.navigationController pushViewController:vc animated:YES];
[self hideProgress]; // addしたviewをremoveする
と書いてあるコードが存在し、もはや期待した動作をしていませんでした。ていうかこんな発想自体ないわー。
超バッドノウハウですが、
HogeViewController *vc = [HogeViewController new];
[self.navigationController pushViewController:vc animated:YES];
vc.view;
vc.hogeLabel.text = @"hoge";
と書いて無理やりviewDidLoad
と呼びだすこともできます。ただしコンパイラの最適化や今後のiOSバージョンによって挙動が変わる恐れがありますし、適切に非同期処理を書くべきです。