iOSアプリって勝手なイメージですけど、webアプリよりもFLASHの方が近いかなーと思っています。なのでFLASHやってたころのProgressionの知識と、最近のRails周りの知識に照らし合わせながらのメモ的な感想的な何かです。
1. パターンとは何か
この本で言うパターンの説明。デザインパターンとかフレームワークなどではなく、スニペットのようなものと理解しました。
2. アプリ設計のパターン
アプリ設計を3つのフェーズで行おうという提案。私が普段やっているのもこんな感じですね。
- 機能設計
- ユーザーインタフェース設計
- クラス設計
MVCの考え方
ビューはモデルに依存とありますが、Railsとかだとコントローラーに依存しているようなイメージがあって、その辺違うのかなーと思いました。モバイル端末だと、ビューはデータ表示のみで独立しているからですかね。
- モデルは、他のレイヤから独立している
- ビューはモデルに依存しており、コントローラから独立している
- コントローラは、モデルとビューに依存している
アプリコントローラ
共通のアプリコントローラを最低1つ用意する。アプリの起動時、終了時の処理などを記述する。
3. モデルのパターン
2つのパターンを定義することを推奨している。
- モデルマネージャクラス
- モデルオブジェクトクラス
モデルオブジェクトクラス
情報を保持することだけに限る。
- 情報の保持
- IDの作成
- アクセッサの提供
- 保存と読み込み
モデルマネージャクラス
インスタンス化したモデルクラスを管理する。シングルトンで管理。これもProgressionのデータ管理手法と似ている。Progressionにおいては、データの管理方法は定義されていなかったが、Progression使っているひとと、シングルトンで管理するよねーというお話した記憶があるし実際私もそうやっていました。
ただ当時は、モデルとモデルトマネージャクラスちゃんと分けてなかったので、ちゃんと役割分担する方がよりオブジェクティブだなと感じました。
役割は以下。
- モデルマネージャの参照の取得
- 自身をどこからでも参照できるようにするってことですな。
- モデルオブジェクトの集合の管理
- データベースでいうとテーブルの保持的なイメージ
- モデルオブジェクトの操作
- データベースで言う、テーブルの操作的なイメージ
- モデルオブジェクトの集合の保存、読み込み
- データの永続化
- 永続化したデータの読み込み
モデルマネージャーの定義は、モデルオブジェクトの階層構造を考慮して上層の部分を定義すると良い。
モデルオジェクトクラスのパターン
イメージ的にはRubyでいうDataMapper的な感じ(使ったことないけど)でインスタンス変数で情報の属性を保持する。
インスタンス変数
indentifierプロパティを用意することで各インスタンスを容易に参照可能とする。これ、Progressionでも各インスタンスにid付けられて便利だなーと思っていたのと似ている件が先に浮かんだだけど、テーブルでいう主キーですね。ふむふむ。
- indentifierはCore FoundationフレームワークのCFUUIDを使う
- インスタンス変数はretainして、このクラスで保持する
保存と読み込み
- NSCodingプロトコルに準拠することでファイルへの保存対応が可能となる。
モデルオブジェクトマネージャクラスのパターン
管理しているモデルオブジェクトへアクセスする手段として以下のメソッドを定義する。配列に対する操作ですね、これ。
- 追加
- 挿入
- 削除
- 移動
4. メモリ管理のパターン
- iOSでのCocoa Touchではガベージコレクタを利用することが出来ない
- Cocoa Touchでは参照カウンタという手法を使って、オブジェクトを管理する
インスタンス変数とオーナーシップ
- インスタンス変数は基本、retainしておき保持する
- デリゲートとなるオブジェクトは保持しない
setterの注意点
- セットしたい値と現在の値が同じ場合、何も考えずにreleaseして retainしなおすとクラッシュするので注意
relase
- releaseしたらnil代入する
dealloc
- 各インスタンス変数のreleaseを呼ぶ
5. ビューコントローラのパターン
ビューコントローラのライフサイクルを理解せよ、というお話から始まる。
ビューコントローラのライフサイクル
- initWithNIbName or initWithCoder
- loadView
- viewDidLoad
- viewWillAppear
- viewDidAppear
- viewWillDisappear
- viewDidDisappear
- dealloc
viewWillAppearでメモリ不足の場合に
- didReceiveMemoryWarning
- viewDidUnload
でアンロードされる。
初期化
initWithNibName:bundle と initWithCoder がある。後者はIBで配置したときに勝手に呼ばれる。共通のinitメソッドを用意しておき、それを呼ぶようにしておくのが良い。
ビューに関連づけが行われるのは、viewDidLoadのタイミング。アウトレット参照が接続されているのでビューに対する初期化はここで行う。nibファイルを利用しない場合はloadViewのタイミングで、viewを自分で設定する。
ビューコントローラの表示
アニメーションの開始前がviewWillAppear、終了後がviewDidAppear
ビューコントローラの隠蔽
アニメーションの開始前がviewWillDisappear、終了後がviewDidDisappear
ビューの解放
メモリ不足のとき、didReceiveMemoryWarning, viewDidUnloadの順に呼ばれるが、viewDidUnloadとdeallocはinitの逆でアウトレットを解放するための共通メソッドを用意しておき、それを呼ぶようにしておくのが良い。
ビューコントローラの配置
出来るだけよけいなインスタンス保持をせずに、オーナーシップを破棄してしまうのがよい。
- autorelaseしてから、ナビゲーションコントローラに入れる
- autorelaseしてから、モーダルビューとして表示する
6. テーブルのパターン
テーブルはCocoa Touchの中核をなす、データソースとデリゲートを利用している。
UITableViewとUITableViewCell
テーブルのカスタマイズは基本、UITableViewCellで行う。
テーブルのMVC
- UITAbleView, UITableViewCell
- UIViewController
- モデルマネージャークラス
モデルのインスタンスとUITableViewCellが1対1となると膨大なメモリを食うのでモデルマネージャクラスを利用してUITableViewCellを再利用する。
セルのカスタマイズ
- みためはサブビューであるcontentViewをカスタマイズする
セルの更新
- 単純にreloadすると状態までリセットしてしまう
- 値だけを更新するメソッドを用意すると良い
7. 通知のパターン
3種類の通知
- デリゲートによる通知(1対1)
- キー値監視による通知(1対多)
- NSNotificationによる通知(多対多)
8. ネットワークのパターン
- 同期通信はメインスレッドで行わず、サブスレットを生成する
- 同時に複数の通信を行い効率化する
- ネットワークアクセス状況を適切に表示する
コントローラから、コネクタ、レスポンスパーサという階層でネットワーク処理を構造化するとよい。基本は非同期通信で実装すべき。
レスポンスパーサ
実際にHTTP通信を行いレスポンスをパースする。
コネクタ
レスポンスパーサを管理し、複数通信の状態を管理する。
主にコードが中心の章。写経した方が良さそうです。
9. iPadへの対応
この本の内容に沿ったパターンで開発していれば、iPad用アプリコントローラを記載するだけで完了。
感想
iOSアプリの開発における設計思想の1つとして大変参考になりました。FlashでProgressionに出会ったときに似ている感じですね。iOSアプリ技術者から1日みっちり講義を受けたような感じで、そう考えると2,940円とか安いもんです。