2008年12月18日木曜日

ようやくiPhoneアプリのパッケージ構成/クラス設計のコツがわかってきました

  • 良いクラス構造を学ぶためには、複数の偉い人のソースを読むのが一番良い

オブジェクト指向言語に限らず、どのようなプログラムを組むにしても、
クラス分割やモジュールの分割は再利用性・保守性を高める上で重要な点だと思います。
もちろん、iPhoneのアプリ開発だってそうです。

Objective-CでCocoa Touchフレームワークを使い始めておよそ1ヶ月、
ようやくこのフレームワークの作法やクラス分割のコツなどがつかめてきました。
まだまだ間違っているところが多数あるような気がしますが、現段階での自分のクラス分割法を晒してみます。

まずはグループ(Javaで言うパッケージ)の分け方から。
私はこんな感じで分けることにしました。ほとんど偉い人のパクりです。
  • Classes
    • Libraries
      各種ライブラリを配置する
      • JSON
      • ImageStore
      • その他自分が作ったアプリのロジックもここに置く
    • Controllers
      View Controllerの類を配置する
      • UIApplicationDelegate
      • UIViewController
    • Views
      カスタムビューを配置する
      • UITableViewCellのサブクラス
      • UIViewのサブクラス

悩ましいのはカスタムビューのライブラリを利用するときなのですが、
一応、Librariesに追加しようと考えています。

自分の書いたロジックはLogicsのようなグループを作成してLibrariesと明確に分けたほうが良いかもしれませんが、
後々自分の書いたロジックも可能な限り使いまわせるようにしたいので、Librariesに入れるようにしています。



クラスの種類を大別すると以下のような感じに。

●View
ビュー。画面。そのまんまですね。

●Controller
UIApplicationDelegateとUIViewControllerがここに属します。データ受け取って画面を書き換えるのがお仕事です。

●Logic
以前はControllerとDataSourceを直接つないでいたのですが、どうしてもうまくいかないので、ControllerとDataSourceの間に1つ層を設けるようにしました。
こいつのお仕事はDataSourceから非同期に受け取ったデータを貯蓄しておき、Controllerにデータを取得するためのインターフェースを提供することです。
データを貯蓄しておけるので、必要なときだけDataSourceからデータを取ってくることができます。
ん?ここまで書いていて思ったけど、LogicというよりLightWeightパターンやProxyパターンに近い?もっといい名前を付けてあげようかな。

●DataSource (Data Access Object, DAO)
実際にデータを取ってくるクラス。HTTPアクセスやらファイルアクセス、SQLiteへのアクセスなどいろいろ手段はありますけど、
いずれの場合でも必ず非同期でデータを取ってくるというところがキモだと思います。

クラス間の結合方法ですが、ControllerとLogic、LogicとDataSourceの間は、Delegateおよびプロパティを使って結んでいます。
Controller -> Logic, Logic -> DataSourceの呼び出しはプロパティからたどってアクセス。
DataSourceは非同期処理を行うので、DataSourceからの返り値はDelegateをたどってControllerまで伝播される仕組みです。
この仕組みは本当に良くできていると思います。非同期処理なのにとても簡単。

現在の課題としては、出来る限り複数のControllerから同一のLogicに対するアクセスを避けるようにしたいのですが、
そのための具体的な方法が良く思いつきません。
Tab barとか作って大規模なアプリになってくると難しそうですねー・・・
delegateだけではなくてCocoaに標準で備わっている通知機能も使っていく必要があるかもしれないと思ってます。

2008年12月17日水曜日

CS193P 11日目 非同期処理をやってみる

  • 非同期処理を行う方法はいくつかある
  • URLフェッチ処理ならば、NSURLConnectionクラスをつかっておけば一発
  • さらに簡単にURLフェッチ処理を行いたいのであればこのライブラリをおすすめ
  • URLフェッチ以外の処理を行うならば、NSThreadを使うか、NSOperationとNSOperationQueueを併用する
  • NSThreadは従来どおり、本当にスレッド処理を記述する必要があるため非常に大変
  • 対するNSOperationはインスタンスをつくってキューにぶち込んだら後は勝手にやってくれる、楽
  • UIViewやUIViewControllerに対する処理(要するに画面に対する処理)は、必ずメインスレッドから呼び出す必要がある
  • スレッドセーフではないため
  • 要するに[object performSelectorOnMainThread:withObject:waitUntilDone:modes:]を使えば解決する

いきなり日付が飛んで11日目です。
このあたりからは課題1つにつき3日分ぐらいのの講義内容が含まれていて、難易度がどんどん高くなってきました。
母さん、おいらスタンフォード大学の学生にはなれそうもないよ。



さて、今回の内容は非同期処理です。
現在の課題ではTwitterのタイムラインをJSON形式で取得して表示を行っているのですが、
メインスレッド(プログラムのメインループが走っているスレッド)の上から直接URLに対してHTTPアクセスを行っているため、
処理が返ってくるまでメインスレッドがブロックされ、結果フリーズしたように見えるという問題がありました。
これを非同期処理にしてブロックしないようにしましょうね、と言うのが今回の課題の内容。

NSURLConnectionと言うクラスを使えばURLのフェッチを自動的に非同期で行ってくれるのですが、
ご丁寧に「NSThreadかNSOperationで処理してね」とご忠告が。
Threadはどうにも使いこなせる気がしないので、ここはより簡単なNSOperationを使おうと思います。
(ゲームなどではおそらくNSThreadを使うことになるんだと思いますが)


NSOperationというクラスを継承して、
mainメソッドをオーバーライドして処理を記述し、
NSOperationQueueに追加すると自動的にThreadを裏で立ち上げて並列処理を行ってくれます。
処理が完了したらKVOという機能を使ってNSOperationから通知を受け取るらしいです。
しかしこのKVOと言う概念がイマイチ理解できないので後回しにして、
より簡単なNSInvocationOperationというクラスを使うことにしました。

使い方はこんな感じです。
NSInvocationOperation *op = [[NSInvocationOperation alloc]
     initWithTarget:self
     selector:@selector(reloadPerson:)
     object:person];
 [self.operationQueue addOperation:op];
 [op release];

これだけで自動的に並列処理をしてくれるんだから凄いと思います。
ということで、今回の課題では以下のように並列構成をしてみました。

スレッド1:メインスレッド
スレッド2:TwitterからTimelineを取得するためのスレッド(NSInvocationOperation + NSOperationQueue)
スレッド3:画像を取得するための並列処理(ImageStoreを利用、内部的にはNSURLConnection)

ところがこれがうまくいきません。
1と2だけを並列処理させたときはうまくいき、1と3だけのときもうまくいくのですが、
1と2と3と並列で動かすとエラーになります。
ああもう!だから並列処理なんて嫌いだ!

デバッガで調査してみるとSocketの取得のあたり?でとまっている感じがしたので、
スレッド2かスレッド3がソケットを捕まえてロックしているのではないかと考え、
使ったらすぐreleaseするようにソースを変えてみたのですが、効果なし。

Google先生にご相談したところ、それらしい回答が。
【iPhone】スレッド中で[UITableView reloadData]を使ってはいけない
なるほど!自分のソースを見直すと、確かにスレッド2の処理の中でUITableViewに対してreloadDataを呼び出しています。
さっそくご指摘のあったとおりにソースを書き直してみました。
if ([delegate respondsToSelector:@selector(mPersonDataSourceDidFinishLoadOfPerson:)])
{
 [delegate performSelectorOnMainThread:@selector(mPersonDataSourceDidFinishLoadOfPerson:) withObject:person waitUntilDone:YES];
}

今度は一発で成功!

2008年12月14日日曜日

第2回 Google App Engine Hackathon

  • 今回で第2回目
  • 主催は@tmatsuoさん
  • 4チーム(内部でさらに分かれるため合計6チーム)に分かれて作業をし、最後に発表する形式
  • 1回目よりも運営が格段にスムーズで素晴らしい時間を過ごせた
  • GDataの認証はすさまじく難しい。またGDataアクセス用のライブラリがあるが、これまた非常に機能が多く難しい
  • Google App Engine Oilは凄く良い、余計なことをしないし必要な者は全部作ってくれる
  • gaeogenはバージョン0.21の地点ではバグがあるためまともに機能しない
  • DropBoxを使ったグループ開発はすさまじいスピード感がある

http://groups.google.co.jp/group/google-app-engine-japan?hl=ja
ちょっと遅くなりましたが、2008年12月13日に開催されました第2回 Google App Engine Hackathonについてご報告です。

主催は前回に引き続きGoogleの認定エキスパートである@tmatsuoさん。
前回は好き勝手にGAEで物を作るだけという感じだったのですが、今回は趣向を変えて、
  • チュートリアル:RSS Reader作成
  • チュートリアル:Twitterもどき作成
  • GDataを用いたマッシュアップ:Bloggerなどとマッシュアップする組
  • GDataを用いたマッシュアップ:Picasaなどとマッシュアップする組
  • フレームワーク
  • データモデル
以上の6組に分かれてそれぞれ活動し、結果を最後に発表すると言う形式で行われました。
目標がはっきりしていたおかげで、最後にはものすごいアプリが次々飛び出しました。大成功だったと思います!
運営の皆さんありがとうございました!
前回に引き続きお弁当おいしかったです =)


詳細につきましてはHiiroさんのブログをご参照ください。
Hiiro_memo: GAEハッカソン参加感想&まとめ:Google凄いが周辺人も凄い


私はマッシュアップ班に参加いたしました。
ちなみに隣の席が世界に名だたるおっぱいプログラマー@technohippyさんでした。びっくりです。
Da変態な人かと思っていたら、意外と普通の人でした。Vistaに泣かされていましたけど。

今回のマッシュアップ班の開発の際には、DropBoxに全員のプロジェクトファイルを配置して、いつでもみんなのファイルを参照できるようにして作業しました。
コレが凄くいいです。誰かが一人難所をクリアできれば、すぐにみんながそのソースを参照して動かせるように出来たため、作業効率が格段に良かったです。
(共有する人はコミット作業すら必要ありません。全部自動的に同期してくれるから)

さっそくDropBoxが大のお気に入りになりました。Windows機にはあまり導入したくなかったのですが、
こんなに便利なら使うしかないですね。


また、噂のGoogle App Engine Oil(GAEO)も試してみました。
gaeo testproject

とすると、testprojectを作成してくれるのですが、コレが素晴らしい!
最初から絶対に必要になるjs, img, cssフォルダを作成してくれたり、faviconの設定もしてくれたり、かゆいところに手が届く感じです。
RequestHandlerの実装も非常に簡単になっていて、
class WelcomeHandler:
  def index(self):
      #get All models from DataStore
      models = AModel.all().fetch()
      #set models to self object to use it when rendering the template
      self.models = models

たったのこれだけで、テンプレートのレンダリングまで全部自動でやってくれます。

本当はgaeogenというrailsのscaffoldのような機能があるのですが、こちらは0.21の段階ではバグがあるようでまともに機能しませんでした。
(ソースを参照したところ、argvに対してgetメソッドを呼び出したりしていました。
Pythonのargvはlist型だからdict型にしか使えないgetメソッドは利用できないです)
でもgaeoだけでも十分すぎるほど便利ですから導入する価値は有りだと思います。