iOS 6からAuto Layoutが導入されてInterface Builder(以下IB)だけで完結するケースも増えたのですが、やっぱりframeを直接設定したほうがコーディングしやすいケースもあるでしょう。

今回はIBを使わずSubviewのframeを直接代入してレイアウトする方法を解説。もちろん解像度の違う端末(iPhone5以前とiPhone5、iPad)にも対応することを前提としています。過去のframeに固定値ベタ打ちコードを引き継ぎつつiPhone 5対応したい場合にも参考になるかも。

※ iOS 5以上、ARC環境を前提として説明していますのであしからず

Subviewをメンバー変数定義するときはweakがおすすめ

まずSubviewをメンバー変数定義する際のお話。Subviewはweakで定義したほうが良いです。そうすると、以下のようにviewDidUnloadで、nilを入れて解放するおまじないが必要なくなります。

- (void)viewDidUnload {
  [super viewDidUnload];
  _button = nil;
}

メンバ変数がいっぱいあると大変ですね。ARCでは楽できます。

weakメシウマな流れを説明。

  • SubviewはSuperview(UIViewController#view)にaddSubviewされ参照カウンタが+1される
  • [super viewDidUnload]が呼ばれると、SubviewはSuperviewからremoveされ参照カウンタが-1される
  • weakの場合、この時点で参照カウンタが0になりSubviewは解放される
  • weakは解放されたオブジェクトを参照していた場合、自動的にnilになる

ちなみにIBでSubview配置しIBOutletで参照する場合も、weakで定義しておくほうが良いです。ドキュメントにもそう書いてあったはず(どこだったか忘れた・・・)

weakでのメンバ変数定義は以下のとおり

@interface MyClass : NSObject {
  __weak UIButton *_button;
}
@end

Subviewの生成とaddSubviewは、viewDidLoadで行います。

- (void)viewDidLoad {
  [super viewDidLoad];

  // ローカル変数に代入することで一時的に参照カウンタ+1
  UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
  // weakなので参照カウンタ変わらず
  _button = button;
  // addSubviewされて参照カウンタ+1 
  [self.view addSubview:_button];
}
// スコープから抜けて参照カウンタ-1
// つまりaddSubviewによる参照カウンタ1の状態
// 親であるviewが解放されると同時にSubviewである_buttonの参照カウンタは0になリ、値はnilになる

コメントにあるように、weakのメンバ変数への代入の前にいったんローカル変数へ代入する必要があることに注意。weakのメンバ変数へ生成したSubviewを直接代入すると、代入後すぐに解放されてしまいます。よってローカル変数代入でいったん参照カウンタを+1しないと泡のように消えてしまいます。Xcodeもwarning出力しますけど。

Subviewの表示・非表示はhiddenプロパティで

動的に表示・非表示されるSubviewはhiddenプロパティを使うと良いでしょう。removeSubviewで非表示してしまうと、weak変数の参照カウンタが0になってSubviewが解放されてしまうので注意が必要です。二度と表示しないのであればそれでよいのですが。

UIViewのSubviewのメンバ変数はstrongでも良い

UIViewを継承してSubclassを生成する場合。その場合Subviewはstrongでメンバ変数定義しても構いません。UIViewとそのSubviewが解放されるタイミングは基本的に同じはずです。よってUIViewControllerのときのように考える必要はありません。

UIViewControllerはそれ自身は解放されないが、View,Subviewのみ解放される状況があるから面倒なのです。(viewDidLoad、viewDidUnloadあたり)

まとめ

  • UIViewControllerでSubviewをメンバ変数にするときはweakがよい
  • いったんローカル変数に代入してからweakメンバ変数へ代入する

その2へ続く