2008年12月20日土曜日

NSURLConnection使用中にbad server certificationエラーが出たときの対処法

  • 1203, NSURLErrorDomain, bad server certificate
  • このエラーはSSL使用時に証明書の内容が不正なときに発生する
  • 要するに自己認証証明書(オレオレ証明書)警告
  • 標準APIにはこのオレオレ証明書警告をオフにする機能がない
  • NSURLRequestのallowsAnyHTTPSCertificateForHost:をオーバーライドすることで回避可能

皆さんも、自宅に自分用のサーバーをお持ちだったり、会社のサーバーに業務でアクセスしたりということがあると思います。
こういうちょっとしたサーバーでは、たいていの場合正式な認証局が発行したものではない、自己認証による証明書、
いわゆる「オレオレ証明書」によるSSH認証が行われています。

便利だし、正式な認証局に頼むとお金が必要になったりでついついやってしまいますよね。
ブラウザからアクセスすると警告が出ますが、無視してしまえばなんてことはありません。

ところが。
iPhoneのアプリからこうした「オレオレ証明書」を利用しているサーバーに対して、
NSURLConnectクラスを用いてアクセスしようとすると
1203, NSURLErrorDomain, bad server certificate

というエラー(NSErrorのインスタンス)が発生し、処理が中断されてしまいます。
Appleさんちょっと厳しいですって。
(たとえば、Pythonでliburlを利用してアクセスしたときはたとえオレオレ証明書でも一切怒られません)
しかも困ったことに、このエラーを回避する方法が標準APIに用意されていません。
対策はただひとつ、「オレオレ証明書なんて使うな、Verisignにお金払え」ということらしいです。

オレオレ証明書なんて許さないぞという決意は大変良く分かりますが、
自宅のサーバーならともかく、会社のサーバーでは自分が勝手に証明書取るわけにもいかず。
困りました。

そこで先人たちがNSURLRequestクラスのprivateなメソッドを利用する回避手段を編み出してくださいました。
http://lists.apple.com/archives/Macnetworkprog/2006/Nov/msg00020.html
http://www.phapper.com/Default.aspx?g=posts&m=8


この方法に従って、NSURLConnectionクラス(およびNSURLRequestクラス)を利用する箇所で、以下のようなクラスカテゴリ実装を行います。
@implementation NSURLRequest(NSHTTPURLRequest)
+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString *)host
{
 return YES; // Or whatever logic
}
@end

これで全てのオレオレ証明書の認証を回避することが出来ます。
もし特定のホストのみを回避したいのであれば、以下のように適当なロジックを組んでやればいいと思います。
@implementation NSURLRequest(NSHTTPURLRequest)
+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString *)host
{
 return [@"ba-tyan.oreore.com" isEqual:host];
}
@end

iPhoneではAdHocによって100人までは自由にアプリを配布することが出来ますので、
App Storeを利用しない、自分専用・内輪向けのハックアプリなんかをこうして作っても面白いですね。
でも、せっかくiPhoneで開発するならApp Storeに挑戦しなければ損!もちろん、そのうち挑戦しますよ?

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だけでも十分すぎるほど便利ですから導入する価値は有りだと思います。

2008年12月6日土曜日

CS193P Cocoa Programming - ソースコードをgithubに公開いたしました

gitの練習もかねてここまでの成果ソースコードを公開してみました。
http://github.com/akisute/akisute_cs193p/tree/master

特に見所はありませんが、一応自分の勉強用として。

githubに自分のリポジトリを作ってコミットしてみる

  • gitとは分散リポジトリ、分散リポジトリについてはこちらのページを参照
  • 要するにローカル上のリポジトリ+github上のマスターリポジトリを用意してくれる感じらしい
  • 手元のリポジトリにコミットするのがcommit、リモート(他人の)リポジトリにコミットするのがpush
  • githubへのpushには公開鍵を利用したSSHによる認証が必要
  • 自分のマシンで公開鍵と秘密鍵を作成し、公開鍵をgithubに追加。秘密鍵をssh-agentとやらを使ってローカルマシンにインストールする

私の周りのTwitter界隈でgithubなる良く分からないシロモノが大流行しているので、私も便乗してみることにしました。
バージョン管理システムの経験はCVSとSubversionのみ、しかも両方ともEclipseから使ったことがあるだけでコマンド操作なんて全く分からないど素人ですが、なに、使ってみれば分かるさ!

まずはgitをMacBook Airにインストール。MacPortを使えば一発です。
sudo port install git-core

これだけなのですが、依存関係がひっじょーーーに多いのでインストールに1時間ぐらい待たされました。

次、githubに自分のアカウントを作ります。これは説明不要。

次、githubに自分用の楽しい楽しいリポジトリを作ります。これも説明不要。分かりやすいです。


できました。名前はakisute_cs193p。そのまんま。

次、画面に表示された念仏を、そのまま何も考えずに実行します。
git config --global user.name "akisute"
git config --global user.email "this_is_my_boomstick@gmail.com"
cd ~/Documents/Xcode/
mkdir akisute_cs193p
cd akisute_cs193p
git init
touch README
git add README
git commit -m 'first commit'
git push origin master

と、最後のコマンドを入力したところでなにやら訳の分からないエラーが出て停止。困りました。
とりあえず何をやっているのかを少しずつ把握していくことにします。

まず最初の2行でコンフィグ。名前とメアドを決めてね、ということらしいので、適当に入力。
次、自分のXcodeプロジェクトがある場所に移動して、リポジトリ用のディレクトリを作って、
git initコマンドを実行してその場にリポジトリを作りました。その場に、と言うのがポイント。
これでgithub上とローカル上に2個の同じリポジトリが完成したことになります。
touchコマンドでREADMEファイルを作成。
READMEファイルがあると、github上でREADMEファイルの中身が表示されるみたいです。
Licenseとか表示するのに便利そうですね。
次のgit add READMEで作ったファイルをgit様の管理下におきました。
git addはファイルを管理下に置くときだけではなくて、コミットの前にも実行する必要がある?みたいです。
git add .(ピリオドを忘れずに)とするとカレントディレクトリ以下の全ての存在をgit様の管理下に置くのだハハハハーらしいです。便利ですね。
変更追加を管理下においたら、git commit -m 'first commit'でコミット。
どうやら-mは引数でコメントを入力するオプションみたいです。
-mオプションを指定しなければ、自動的にvimが立ち上がってコメントを求められます。
コメントなしのコミットは出来ません。

最後、問題のgit push origin master。一体全体何をしているのか分からなかったので、まず調査してみました。
http://github.com/guides/git-cheat-sheet
ここでgit pushの例を見てみると、リモートリポジトリoriginに対して、ブランチ名masterをコミットしているらしいです。
要するにここでは、github上に一番最初に用意したリポジトリに対して、既存のブランチmasterをコミットするということかな。
どうして自動的にgithubに対してpushしてくれるのかはわかりませんが、きっと最初の設定がそうなっているんでしょう。

では分かったところで、よく分からない理由でgit push出来ない問題を解決しましょう。まずは適当に検索・・・
git/github - TOBY SOFT wiki
ヽ( ・∀・)ノくまくまー(2008-06-02)
すると偉大な先人たちが既に答えを導いておられました。素晴らしい!
要するに、git pushの際にSSHの認証をしたいんだけど、
秘密鍵と公開鍵がないから認証できないよ、ということらしいです。

まずは鍵を作ります。以下、@ITからコピペ&一部改変。
@IT:sshでパスワードなしにログインするには
ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/akisute/.ssh/id_rsa): ←[Enter]キー
Enter passphrase (empty for no passphrase): ←パスワードを入力
Enter same passphrase again: ←同じパスワードを入力
Your identification has been saved in /Users/akisute/.ssh/id_rsa.
Your public key has been saved in /Users/akisute/.ssh/id_rsa.pub.

できました。
次、githubに戻って、accountページから公開鍵を登録します。
less ~/.ssh/id_rsa.pub

この結果を何も考えずコピーして以下の画面に貼り付ける。


できました。改行とか入っていないかだけは注意。改行したらダメらしいです。

@ITの例では、やれchmodしろだの鍵を作ったらどうのこうのしろだのとか書いてますが、無視。
要するにサーバー側に公開鍵を置いて、クライアント側に秘密鍵を置いておけばいいようです。
今回はgithubがサーバー様なので、githubに公開鍵を渡せばいいってことですね。

次。以下のコピペ呪文を詠唱します。何も考えずに力を抜いて楽にして。
eval `ssh-agent`
Agent pid 12345
ssh-add ~/.ssh/id_rsa
Enter passphrase for /home/.ssh/id_rsa: ←パスワードを入力
Bad passphrase, try again for /home/.ssh/id_rsa: ←同じパスワードを入力
Identity added: /home/.ssh/id_rsa (/home/.ssh/id_rsa)

できた。凡人凡妖怪の私には何が起こったのかすらわかりません。
注意点はただひとつ、`ssh-agent`の`は'じゃなくて`です。Shift+@。

このコマンド入力によってgithubへの認証が可能になる!らしいです。早速試してみます。
git push origin master

おお!今度は成功!
調子に乗っていろいろ追加してみます。
git add Presense
git commit -m 'first commit'
git push origin master

これで俺もgitマスターだぜ!と調子に乗っていたら、


出ました、Mac OS Xの恥部(と勝手に自分が思っている)、.DS_Storeです。
これはリポジトリに含めたくないですね。どうすればいいんでしょう?
答えは簡単、.gitignoreというファイルを作って、お守り代わりにリポジトリのルートに配置すればいいらしいです。
build
.DS_Store
*.o
*.ob
*.pbxuser
*.tmproj
*.model*
*.mode*
*.build

このお守りを置けば、git add時にここに書いてあるパターンにマッチするファイルはaddされないらしいのですが、
すでにaddされてしまったものについては自分で消すしかありません。
git rm .DS_Store
git commit -m 'removed DS_Store'
git push origin master

まぁ、ざっとこんなもんよ、なんてね。

CVSやSubversionと比べると、ソースコードを公開するのが非常に楽でいい感じです。
ネットワーク環境が無くてもローカルのリポジトリに対してコミットできると言うのも地味に嬉しいところ。
過去の変更履歴を見たりブランチを切ったりするコマンドについては、おいおい学んでいこうと思います。

2008年11月30日日曜日

DEMOsa Vol.5に参加してきました

DEMOsaについてはこちら
http://www.mosa.gr.jp/?p=2168

デザイナー系の人が自分のやった新しくて面白いことをライトニングトークみたいに発表する会でした。
スケジュールを見てもわかる通り、iPhoneネタが非常に多かったです。

最初の半分を遅刻して見逃してしまいましたが、以下感想を箇条書き。

■ポメラ
  • ノートPCが持ち込めない場所(会社とか会社とか会社)では大活躍できそうだ。言われてやっとその価値に気づいた。
  • でもポメラからPCにデータが移せない(USBメモリは全面禁止が当たり前なので)ので、結局意味がない気がしてきた。
  • 実際に触らせていただいたが、小さい。初代DSにそっくり。キーボードはいまいち。EeePCのどうしようもないキーボードに比べれば遥かによいが、MacBook Airのキーボードに慣れてしまうとやはりMacBook Airに軍配が上がる。

■Cookie
  • 絵文字だけでコミュニケーションする子供向けSNSのようなもの。
  • 実装はともかくとして、絵文字だけでコミュニケーションという発想は最高。これでイスラエル人ともヘブライ語を覚えずしてコミュニケーションできる訳だ。絶対にこれはiPhoneで実現するべき。

■iKotoの中の人
  • 日本文化に関するアプリばっかり作る理由は、文化を創るか。かっこいい。
  • 漫画とアニメ以外の文化は創らないと伝わらない。

■Afrous
  • ブラウザだけでマッシュアップが作れるというアプリ。らしい。
  • RSSもAPIもないただのWebページのDOMを解析してデータを抜き出して、youtubeやamazonと連携させていた。凄い!
  • たとえば日経新聞のトップページを切り出して強制的にRSS化することもできそうだ。

■RainbowNoteの中の人
  • 1万アプリものアプリケーションがあふれ変えるiTunes Storeの壁は高い、キーになるのはプロモーション
  • Moopf's App Store Review Scrapper itunesレビュー評価を見る
  • Mybefia 競合アプリとの人気の差を見る
  • Mobclix 国別の評価を見る

■BiND
  • 10分でWebページを作成していた。Webページをブロック単位に分割して、ブロックを追加したり編集したり。
  • 本当なら6時間かかる作業を10分でやったんだって!本当かどうかは信じがたいが、確かにページの編集速度は速かったしCSSをいっさい触ってなかった。
  • ロゴ画像の編集は本当に便利そうだった。

■MemoryTreeの中の人(宮田さん)
  • 空間にPostitみたいに画像を貼付ける!セカイカメラみたいだなぁ。もっともセカイカメラと違ってこちらは本当に実現されている(できることが限られているけど)。
  • iPhoneを振ってメモリーを投げ、メモリーを受け取るというアイディアがすばらしい!後から懇親会でお聞きしたところ一人の天才プログラマーとセットで1週間不眠不休でやって作ったそうな。
  • Motion Comics Playerなんて動画を電子書籍としてリリースしているけどほとんど売れてないらしい。それでもそこそこのApp Storeで上位だから電子書籍はよほど売れていないんだろうとのこと。
  • 以下、後から懇親会でお話ししたときのこと・・・
  • 貫禄がある人だった。ちょっと怖い。でもいい人。
  • 1万アプリのうちほとんどは有象無象、いいものを作ってプロモーションをきちんとやればいくらでも上位に入れるよ、とのこと。
  • ケータイなんかに比べてiPhoneを購入する層の嗜好は偏っているとのこと。いわゆる新しいものとかっこいいものが好きな人たち。
  • 実はこの宮田さん、世界で一番最初に着メロを作って大もうけした人なんだとか。なんてこった・・・
  • で、あまりにも着メロが大成功だったのでモバイルからは遠ざかっていたが、iPhoneでモバイルに復帰したそうな。それほどの可能性か!iPhone
■Niconの新製品は化け物か
  • http://www.nikon.co.jp/main/jpn/whatsnew/2008/1007_up_01.htm
  • 第一印象:どう見てもスカウターです
  • 第二印象:やりやがったNicon、やりやがった
  • 実際に体験しましたが、ヘッドフォン部分のフィット具合は凄くいいです。画質も凄い、目の前に画像イメージがわいてくる感じです。サイズは640*480らしいですが実際はもっと小さく見えます。
  • ディスプレイ位置の調整がかなり大変です。あと振動にも弱いです(大きくぶれて見えなくなるので、歩きながらはつらいと思います)。
  • しかしこれにGPSとアプリケーション作成プラットフォームをつけてiPhoneみたいにしたら面白そう
  • そうしたら私は絶対にライトセイバーアプリならぬスカウターアプリを作る。「戦闘力、たったの5か・・・ゴミめ・・・」

■ノルウェーの留学生の方とお話
  • 外人さんは未だにちょっと苦手かも
  • 彼曰く、留学している日本人とそうでない日本人は話す内容が全然違うらしい
  • 留学していない日本人は、日本の否定的なところを言われたときに鵜呑みにしてしまうが、留学している日本人は嫌な顔をする(そうではないという)らしい。う・・・

CS193P Cocoa Programming - 7日目 UINavigationViewControllerを使う

  • UIViewControllerのawakeFromNibは動作しないときがある。UIViewControllerの初期化には 必ずviewDidLoadを使うこと
  • UINavigationViewControllerはxibファイルに含めずに、ソースコード中で初期化したり管理する方がうまくいく
  • UINavigationViewControllerの上に表示されるバーを編集したいとき(ボタンを追加したりするとき)は、UINavigationViewControllerにpushされるUIViewControllerのnavigationItemプロパティを編集する。Interface Builderから操作できるかどうかはわからない
  • UIAlertViewがポップアップ表示、UIActionSheetが下からにょきっと出てくるボタンのリストを表示
  • ボタンにソースコード中からアクションを追加することが可能(UIButton, UIBarButtonなどのドキュメントを参照のこと)


さて、ナビゲーションがついてようやくiPhoneアプリらしくなってきました。
画面はナビゲーション時にUIViewControllerのインスタンスがどのように管理されているかをログに吐いてみたところです。viewDidDisappearの後にdeallocが毎回流されているのがわかります。
要するに、一つのviewを毎回毎回alloc->initしないでインスタンスを再利用してやろうと思うときはUINavigtionViewControllerにpushするだけではなくて、どこか別のところでUIViewControllerのインスタンスを保持しておく必要があるようです。2tchの作者さん曰くalloc->initは相当重い動作らしいので、できるかぎり使い回しができるようにしたいです。



ポップアップも出るようになりました。


※いい加減開発ペースと学習ペースをあげようと思っているので、しばらくの間はブログの更新がこんな感じで適当になりそうです。

2008年11月26日水曜日

CS193P Cocoa Programming - 6日目おまけ、多角形をくるくる回せるようにしてみた

  • CS193P(http://www.stanford.edu/class/cs193p/cgi-bin/index.php)のチュートリアルで作っているHelloPolyプロジェクトを自分なりにアレンジしてみた
  • 自分なりにアレンジしてみた=ニコニコ動画だと駄作フラグ
  • UIViewはhiddenプロパティをYESにした瞬間に消えてしまうので、アニメーションでフェードアウトさせたいときは、まずアニメーションだけ実行>アニメーション終了時のデリゲータ(- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag)で実際にUIViewをhiddenにする
  • タッチ動作を取得するには、UIView(正確にはUIResponder)のtouchBeganメソッドやtouchMovedメソッドをオーバーライドする
  • デフォルトではマルチタッチ不可能(最初の1タッチのみ感知する)、マルチタッチしたければ設定を変えること
  • UITouch.tapCountでタップした数を取得できる。これを使ってダブルタップを検出できるが、連打すると3とか4とか2より大きい数が取得されてしまうので注意
  • CGPointはクラスではなくて構造体、頭にCGがつくCore Graphics系はすべて純粋C言語であるところに注意!
  • CGPointなどを作成するときはCGPointMake()関数を使う
  • CGPointやCGRectなどをObjective-Cのクラスとして扱いたいときはNSValueクラスを使う、たとえば[NSValue valueWithCGPoint:(CGPoint)point]など
  • CGRectGetMidX()関数とか地味に超便利
  • C言語の変数の有効範囲について:http://www.cis1.c.dendai.ac.jp/c_master/C_14.htm
    CGPointMake()で作ったCGPointなどは自動変数なので、別のメソッドに渡すときはポインタ渡しではなくてそのまま値で渡す

ただ単にカリキュラムにそって進めていくだけでは面白くないので、この辺りでちょっとチャレンジングなことをしてみることにしました


まずは新しくサブビューを追加。


ON OFFスイッチでビューを出したり消したり。出したり消したりするときはアニメーションします。


UISliderを使って、線の太さを変えてみましたよ。
UISliderのvalueプロパティはdouble型なので注意です。ずっとNSString型だと思ってました。
それから、タップした向きに多角形を回転できるようにしました。赤線は中心からタップした点への線分です。
タップしてドラッグするとスムー(?)ズに回転しますよ。


破線への切り替えもできるようにしました。
UISegmentedControlを使っています。このUISegmentedControl、取得できる値が選択されているセグメントのインデックス番号(selectedSegmentIndex)だけなのでちょっと厄介です。HTMLのラジオボタンみたいに好きな値をセグメントごとに持たせられればいいのに。


今回作成したアプリのプロジェクトファイルを公開してみました。
面白いことをやっている点は何一つないのですが、まぁ一応。
http://sites.google.com/site/akisutesama/files/HelloPoly-06b.zip?attredirects=0

今後はgithubとかで公開できるようにします。

2008年11月22日土曜日

CS193P - ここまでの進捗をアップしてみた

ここまでのCS193Pの進捗をアップしてみました。

http://sites.google.com/site/akisutesama/files/HelloPoly-06.zip?attredirects=0

Xcode3.1用のプロジェクトファイルと、ソース一式が入っています。
Max OS Xで解凍してXcodeで読み込んでコンパイルできると思います。たいした内容ではありませんが一応。

CS193P Cocoa Programming - 6日目、NSUserDefaultsの使い方

  • アプリケーションが終了した後もデータを保持したい(例:現在開いているページなど)
  • NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  • あとはNSDictionaryとほとんど同じ(格納の仕方も取得の仕方も)
  • BOOLとかintをそのまま格納できるメソッドもあります
  • 格納するタイミングは、値がかわった瞬間かまたはアプリが終了する瞬間
  • 取得するタイミングは、アプリが立ち上がった瞬間かまたはawakeFromNibメソッド
今日はNSUserDefaultsとやらの使い方を学習。
NSUserDefaultsとはアプリケーションが終了した後もデータを保持することができる仕組みのようです。
本格的にデータを保存したいならSQLiteやファイルを使うんでしょうが、ちょっとしたデータ(例えば現在開いているページ)を保持したいときなどにSQLを書くのは煩雑です。そういうときに便利に使える仕組みらしいです。

○値を格納する
タイミングは二つ。値がかわった瞬間か・・・
//ボタン押し下げ時に値を減らす
- (IBAction)decrease {
int currentNum = polygonShape.numberOfSides;
currentNum--;
polygonShape.numberOfSides = currentNum;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:currentNum forKey:@"numberOfSides"];
[self updateInterface];
}
またはアプリケーションが終了するタイミングか。
//@implementation HelloPolyAppDelegate(アプリケーションのデリゲートの中で)
- (void)applicationWillTerminate:(UIApplication *)application {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:controller.model.intValue forKey:@"numberOfSides"];
}
パフォーマンスに応じてどちらか好きな方を選んでくださいとのことです。

○値を取得する
こちらもタイミングは二つ。アプリケーションが立ち上がったタイミングか・・・
//@implementation HelloPolyAppDelegate(アプリケーションのデリゲートの中で)
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int storedNumberOfSides = [defaults integerForKey:@"numberOfSides"];
controller.model.intValue = storedNumberOfSides;
[window makeKeyAndVisible];
}

または対象のクラスのawakeFromNibメソッドの中か。
//ここではコントローラクラスの中で・・・
- (void)awakeFromNib {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int storedNumberOfSides = [defaults integerForKey:@"numberOfSides"];
self.model.intValue = storedNumberOfSides;
[self updateInterface];
}

この二つの違いは正直わかってません。複数のNibファイルを利用するときはawakeFromNibでないと駄目らしいですが・・・

CS193P Cocoa Programming - 5日目、いよいよView自作

  • Interface Builderを使ったらとにかくWrite Class Files...を押すのを忘れないように
  • 描画はJavaのSwingなどとほぼ同じ
  • drawRectメソッドをオーバーライドして描画する
ViewとかAnimationとか、いよいよ実践が近くなってきたようです。
だんだんと難易度も上がってきました。



まずは自作Viewを作成。
Interface BuilderからUIViewを引っ張ってきて画面上に配置。
クラス名を適当に(ここではPolygonView)に変更。
ControllerクラスやらModelクラスなど、必要に応じてOutletを追加します。
最後に(一番忘れやすいんだけれども)メニューからFile > Write Class Files... この書き出しを行わないとXcode上に実際のソースが出てきません。

書き出しを行ったらXcodeを開いて、以下の2つの仕事を行います。
・先ほど追加したPolygonViewのスーパークラスを定義する
・ControllerにIBOutlet PolygonView *polygonViewを追加する

新しいオブジェクトはWrite Class Filesで一発だと思うけれども、既存のクラス(例えば前回実装したController)なんかは、書き出ししちゃうと上書きしてしまいそう。怖いので今回はXcodeから書くことにしました。でもこれ非常に非効率的。hファイルだけ上書きしてほしいんですけど・・・



勇気を振り絞って上書きWrite Class Files...にチャレンジしたら、なんだかこんなFileMergeとかいうアプリが立ち上がって、きれいにマージすることができましたとさ。よかったよかった。


では早速Viewに描画処理を追加します。
描画を行うのはUIViewの-(void)drawRect:(CGRect)rectメソッドですので、こいつをオーバーライド。
あとは再描画したいタイミングでUIViewのsetNeedsDisplayをコールすれば適切なタイミングでシステムが再描画してくれるというしくみ。Swingに似てますね。



実際に描画してみました。簡単簡単!!



描画のやり方さえわかってしまえばこっちのものです。
ポリゴンの点の位置を計算するメソッドは例題の中で用意されていたので、それを丸コピーして点の位置に線を引くだけ。線の太さを調節する関数はCGContextSetLineWidthというのが見つかったのでそれを使うだけ。リファレンスが使いやすい!すてき!



カスタムビューの中にUILabelを追加して、ラベルにポリゴンの名称を表示できるようにしてみました。UILabel.textプロパティを書き換えるタイミングはカスタムビューのdrawRectの中で。ほかに良いタイミングが見当たらず。


ひとまずこんなところですかね。次は・・・
  • スライダーで線の太さを変える
  • 線の種類を何らかのスイッチで変える
  • ビューの上でフリックしたらポリゴンがくるくる回るようにする(アニメーション処理の勉強が必要)
これらを試してみたいですね。