2011年3月8日火曜日

lipo を使って簡単に Universal Binary を作成する方法

iOS 向けのライブラリやフレームワークは、よく static library (.aファイル) の形式で配布されています。これは iOS がユーザーが作成した dynamic library (.dylibファイル) や framework バンドルをサポートしていないからなのですが、ときどきこの static library がシミュレーターとデバイス両方で使える形式、いわゆる Universal Binary になっていない場合があります。

たとえばこんな感じですね。



この状態でビルドを行うと、シミュレーター向けビルドを行えばデバイス用のバイナリが、デバイス向けビルドを行えばシミュレーター用のバイナリが、それぞれ対応していないアーキテクチャであると警告を出してしまいます。警告ですからコンパイルは通るのですが、私は几帳面で気になってしまうので、これを解消したいと考えます。


■lipoの出番

そこで lipo を使います。 lipo は Xcode に付属されているコマンドラインツールですので、 iOS SDK をインストールしている人でしたら誰でも使えます。詳しい使い方は man を見ていただければわかりますが、普通使うのは以下のパターンだけです:
lipo -create ライブラリ1.a ライブラリ2.a -output 出力するライブラリ.a
たったのこれだけで二つのライブラリを組み合わせて Universal Binary を生成してくれます。さっきの画像の例ですと、
lipo -create libAbesi_dev.a libAbesi_sim.a -output libAbesi.a
これでOKです。


■本当に Universal なの?

それでは本当に Universal Build になったのか見てみましょう。 static library の中身を見るには、 nm コマンドを使います。



どうやらうまくいったみたいですね。一つのライブラリファイルの中に、3種類のアーキテクチャに対応したオブジェクトが格納されています。

2011年2月28日月曜日

UIWebView のスクロールを制御するためのプロパティを書いてみた

UIWebView にどうして scrollEnabled プロパティがついてねえんだ Apple のチンパンジー野郎!とお嘆きの全国1000万の iOS 開発者の皆様、こんばんわ。もちろん私もその一人であります。

嘆いていてもしょうがないので何とかスクロールを制御する方法を・・・と思って探していたら、すでに2009年の地点で @nakamura001 さんがこんなブログを書いてらっしゃいました。

http://d.hatena.ne.jp/nakamura001/20090520/1242837408

が、遷移先で詳解されている

http://praveenmatanam.wordpress.com/2009/04/03/how-to-disable-uiwebview-from-user-scrolling/

のコードが正直いまいちなのです。何がいまいちって、せっかくのCocoa環境であるにも関わらず、わざわざobjc/runtime.hなんていう低レベルなC言語の関数を使っています。別にパフォーマンスがタイトな場所でもないですし、かっこよくCocoaっぽく書き直してみました。ということで書き直したコードがこちら↓

https://gist.github.com/846258

で、このAdditionを導入するとですね、
UIWebView *webView = [[[UIWebView alloc] initWithFrame:frame] autorelease];
webView.webViewScrollEnabled = NO;
みたいな書き方ができてハッピーになれます。

内部的には NSInvocation を使っています。 Mac OS X 10.5 (iOS 2.0) から存在するこのクラス、本当に便利で、ぶっちゃけ呼び出し対象のシグネチャさえわかれば何でも呼び出せるスグレモノです。フレームワークを作るときなど、呼び出し先のシグネチャしかわからない状況下で対象のメソッドを呼び出す時などに便利な感じかもです。その上 NSInvocationOperation を使ってそのまま並列化もできたりして。
NSMethodSignature *sig = [subview methodSignatureForSelector:selector];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:sig];
[invocation setTarget:subview];
[invocation setSelector:selector];
[invocation invoke];
BOOL result = YES;
[invocation getReturnValue:&result];
return result;

2011年2月20日日曜日

github で pull request をされたとき・するときの手順

github に自分のリポジトリを公開していると、たまに pull request をされることがあります。また逆に、他人のリポジトリのコードを使っていて、どうしても気になるバグを見つけて修正したときなど、相手に pull request を送りたいことがあります。こんなときにどうすればよいかをまとめてみました。


■pull request をしたいとき

pull request をしたいときは、まず相手のリポジトリを fork する必要があります。


このボタンをぽちっとな。

fork したら、 fork して自分の管理下に入ったリポジトリを clone して、コードを修正します。
git clone https://akisute@github.com/akisute/asi-http-request.git
コードの修正が終わったら、自分の fork したリポジトリに push しておきます。
git push origin master
ではコードの修正が終わったので pull request をします。


このボタンをぽちっとな。


するとこうなります。デフォルトでは相手の master に対して自分の master を pull request することになりますが、もちろんこの画面で変更可能です。 request をする先のリポジトリ・ブランチと、リクエストされるブランチをそれぞれ指定できます。それからコメントをつけて送れます。ここでつけたコメントは相手側のリポジトリの Pull Requests タブに記載されるので、それなりにかっこいい内容を書いておくと良いかと思います。あとは相手が受け入れてくれるのを待つばかりです。自分のコメントにコメントがついたらメールとかで通知が来ますんで、適当に返事して相手と連絡を取りながらやる感じになります。


■pull request をされたとき

逆に自分が pull request をされることもありますので、そのときの対処法も。


pull request されると、こんな風に Inbox の Notifications の中に通知が来ます。


対象のリポジトリの Pull Requests タブにもこんな具合でリクエストが入ります。


クリックしてリクエストの詳細を開きます。ファイルの diff 一覧とかどっからどこに対してリクエストが飛んでいるのよとか全部ここで見られます。相手のコメントに対してコメントを返したり、 diff に対してコメントをつけたりできます。 diff につけたコメントは相手にも公表されます。

ここで相手とコメントやりとりとかして、じゃあ取り込もうか、となりましたら、実際に pull request を取り込む作業を行います。自分のリポジトリにマージ用のブランチを用意して、 pull して確認し、問題なければマージ用のブランチと本線をマージして終了。コマンドがよくわからなくても github が全てのコマンドを表示してくれるのでそれを見れば一発です。本当に親切。以下、 master ブランチに対して pull request があったとすると、
git checkout -b pull-request-master master
git pull http://github.com/applicant/his-repository
# 相手のコミットが自分のブランチに pull されてくるので、この状態で動作を確認する
# merge 作業が発生するならここで行う
# 問題がなさそうなら
git commit
git merge master
git push origin master
push まで完了したら先ほどの pull request のページから取り込み完了したよボタンを押しておきましょう。


■おまけ: 自分が fork しているコードの fork 元リポジトリが更新されたので、こちらも最新にしたい

実はこれは pull request をされたときとあんまり変わりません。対象の相手のブランチを自分の fork しているコードに pull すればよいのです。
git checkout -b merge-master master
git pull http://github.com/parent/parent-repository
# 相手のコミットが自分のブランチに pull されてくるので、この状態で動作を確認する
# merge 作業が発生するならここで行う
# 問題がなさそうなら
git commit
git merge master
git push origin master

2011年2月12日土曜日

Redmine を Debian 5.0 にゼロからインストールしてみた

あらかじめ注意: 下記のメモは、ruby, gem, rails, rack, redmineのいずれかのバージョンが0.0.1でも違うと動作しない可能性が高いので、当てにしないでください。


■顧客が説明した要件
  • 1つのサーバーに複数のプロジェクトのリポジトリとかリソースをおいているので、1プロジェクトにつき1個インスタンスが必要なtracは都合が悪い!
  • っていうかtrac飽きた!!古い!
  • 複数プロジェクト扱えるBTSが欲しい
  • 見た目クールだといいね、カスタマイズとかできるとすごくいい!
  • Wikiは絶対必須
  • Google Appsと連携できると素敵だな!
  • 簡単にインストールしたい、時間かけたくない


■プロジェクトリーダーの理解
  • https://www.gravitydev.com/ はすごくいいんだけれど許可下りなさそうだなぁ。そもそもWikiがないから却下。
  • となるとやっぱりRedmineかな!あれすごくいいよね!
  • Redmineに移行しようかみたいな話も社内で出てたし、先んじて導入してノウハウ作るのはありだな!


■アナリストのデザイン
[11/02/10 9:24:53] akisute: redmineのインストールが超面倒くさいんだけれど
[11/02/10 9:24:54] akisute: なんとかならんのこれ
[11/02/10 9:25:02] wozozo ( ゚Д゚)y─┛~~: rails
[11/02/10 9:25:19] tokibito: rails
[11/02/10 9:25:20] 文 殊堂: まいんちゃん
[11/02/10 9:26:39] akisute: http://redmine.jp/guide/RedmineInstall/
[11/02/10 9:26:43] akisute: これ見てるとアホかと言いたくなってくる
[11/02/10 9:26:50] akisute: Ruby 1.9には対応していません。 上記の表に示したバージョンのRuby 1.8.xをインストールしてください。
RubyGems 1.3.1以上が必要です。
Rake 0.8.3以上が必要です。
Rack 1.0.1が必要です。1.0.1がインストールされていない場合、データベースマイグレーションが失敗します。
[11/02/10 9:27:25] wozozo ( ゚Д゚)y─┛~~: aptでいれたgemをupdateしようとすると、
[11/02/10 9:27:34] wozozo ( ゚Д゚)y─┛~~: おい待てって言われるし
[11/02/10 9:27:49] aodag: Redmineってライトニングなのないの?
[11/02/10 9:27:53] tokibito: rubyはaptで入れたら死亡する可能性
[11/02/10 9:28:04] aodag: rvmっしょ
[11/02/10 9:28:04] tokibito: 自分でビルドしたほうが無難とかいう
[11/02/10 9:28:11] tokibito: rvmで。


■プログラマのコード
  1. redmine:redmine ユーザーを新規に作成する
    • 以下の作業はすべてredmineユーザーで行う
  2. rvmの最新をインストールする
    • http://rvm.beginrescueend.com/rvm/install/
    • curl と git-core のインストールが必要。aptitude経由で実施
    • /home/redmine/.bashrcの追記を忘れないように
    • source ~/.rvm/scripts/rvm も忘れないように
  3. rvmに必要なパッケージをインストールする
    • こんなんデフォルトで用意しろよ・・・いいかげんにしろ・・・
    • rvm package install readline
    • rvm package install iconv
    • rvm package install zlib
    • rvm package install openssl
    • この順番で入れないと依存関係の問題で怒られます (opensslがzlibを使うとか)
  4. ruby 1.8.7 をrvm経由でインストールする
    • 参考は http://d.hatena.ne.jp/masasuz/20100720/1279635055
    • 先に rvm package install しないとgemが使えない, zlibがないので
    • ビルドオプションを付けることでopensslとzlibを有効にすること
    • rvm install 1.8.7 -C --with-openssl-dir=$HOME/.rvm/usr -C --with-zlib-dir=$HOME/.rvm/usr
    • rvm use 1.8.7
    • ruby --version
    • gem --version
  5. gemのバージョンを 1.3.7 に戻す
  6. 本当にopensslとかzlibとかインストールされているかをチェックする
    • ruby -r openssl -e ""
    • エラーになった場合はopensslの再ビルドを行う。opensslを使えるようにしないとのちのちrakeを実行するところでエラーが発生して先に進めなくなる
    • cd ~/.rvm/src/ruby-1.8.7-p330/ext/openssl
    • ruby extconf.rb --with-opt-dir=$HOME/.rvm/usr/
    • make
    • make install
    • ruby -r openssl -e "" を再度実行してエラーにならない事を確認
  7. rails 2.3.5 をgem経由でインストールする
    • gem install rails -v=2.3.5
    • rails 2.3.5をインストールすると自動的にrack 1.0.1が入るが、先にrack 1.0.1をインストールしておいたほうが良いかも
  8. rack 1.0.1 をgem経由でインストールする
    • gem install rack -v=1.0.1
  9. sqlite3-ruby 1.2.3 をgem経由でインストールする
    • 参考は http://d.hatena.ne.jp/modka/20101208
    • sqlite3-devel (sqlite3.h) が見つからないとインストール出来ないので注意
    • 最新のsqlite3-ruby は sqlite3 3.6.3 が必要で、Debianのaptitudeでは3.5.9までしか無いので、バージョンを指定して古いsqlite3-rubyを入れる
    • sudo aptitude install sqlite3
    • sudo aptitude install libsqlite3-dev
    • gem install sqlite3-ruby -v=1.2.3
  10. i18n 0.4.2 をgem経由でインストールする
    • rakeの実行に必要
    • rakeお前依存関係に含めろや・・・アホか・・・><
    • gem install i18n -v=0.4.2
  11. redmine のリリース版の最新をダウンロードする
  12. データベースの設定を行う
    • 参考は http://redmine.jp/guide/RedmineInstall/
    • 今回はsqlite3
    • cd ~/redmine-1.1.1/config
    • cp database.yml.example database.yml
    • vim database.yml
    • cd ~/redmine-1.1.1
    • rake generate_session_store
    • rake db:migrate RAILS_ENV="production"
    • rake redmine:load_default_data RAILS_ENV="production"
  13. メールの送信設定を行う
    • 不要と思われる。メールが飛ばないだけで済む
  14. 起動する
    • cd ~/redmine-1.1.1/
    • ruby script/server webrick -e production
    • http://localhost:3000/ が立ち上がるので、 admin/admin でログインする
    • 適切にadminのパスワードを変更すること
  15. ログの設定を行う
    • やっとかないとログが溜まりまくって死ぬらしいので
    • cd ~/redmine-1.1.1/config
    • cp additional_environment.rb.example additional_environment.rb
    • vim additional_environment.rb
    • http://redmine.jp/guide/RedmineInstall/ のログの設定セクションのとおりに設定しておく
  16. Passengerをインストールする
    • 参考は http://d.hatena.ne.jp/Umeyashiki/20100131/1264948069
    • gem install passenger
    • passenger-install-nginx-module
    • ここまでの手順どおりに進めていると、大体以下のものが足りないはず
      • Curl development headers with SSL support... not found
      • OpenSSL development headers... not found
      • Zlib development headers... not found
    • 一旦abortしてそれぞれインストールすること
      • sudo aptitude install zlib1g-dev
      • sudo aptitude install libssl-dev
      • sudo aptitude install libcurl4-openssl-dev
    • nginxの再ビルド(ないしソースコード)が必要になるので、すでにapt-getしたnginxがある場合には使えない。その場合はpassengerをstandaloneで起動して、そこに対してnginxからproxy-passなりなんなりする。
    • 以下、passengerスタンドアロンで起動し、redmineはすべてpassengerにやらせる場合を考えます。ただしpassengerのスタンドアロンは内部的にはnginxを使っているので、いずれにせよ上記のインストールは必要になります。
    • cd ~/
    • mkdir log run
    • passenger start redmine1.1.1 -d -a 0.0.0.0 -p 3000 -e production --log-file=log/passenger.log --pid-file=run/passenger.pid
    • ってやると何故か/tmp以下にlogファイルをつくろうとしていみわからないエラー吐いて死ぬので中止
    • cd ~/redmine1.1.1
    • passenger start -d -e production
    • 最低限のオプションだけで起動すれば起動できた


■営業の表現、約束
  • お客様、 redmine をお求めですか?ご安心ください! gem install redmine で一発に決まってますよ!簡単です!
    • そんなものはない
  • それなら sudo apt-get install redmine で一発です!猿でもインストールできます!!
    • Debian では無理
  • それなら最初から redmine がインストールされている vm イメージがあります!こちらを使えば一発です!
    • Ubuntu と SUSE しかない
  • Trac Lightning とかゆとり。真のエンジニアは ruby の configure & build から自分でやって当然(キリッ
    • そんな業務と関係ないところに何時間(何万円)使うつもり?
    • アップデートどうすんの?何時間(何万円)使うつもり?


■プロジェクトの書類

http://redmine.jp/guide/


■実装された運用

http://www.redmine.org/projects/redmine


■顧客への請求金額

akisute の実働5時間分 = 数万円 = https://www.gravitydev.com/pricing のスタンダードプラン数年分


■得られたサポート

http://redmine.jp/guide/RedmineInstall/


■顧客が本当に必要だったもの

http://twitter.com/shin_no_suke/status/35632371084562433
http://twitter.com/hirokinko/status/36307730025156608
http://twitter.com/hirokinko/status/36310634127695872


■元ネタ

http://ryonoda.wordpress.com/2008/09/26/


■一応補足

Redmine自体はすごく良いアプリだと思います。ええ。いま使ってますが、とっても助かってます。

bitnamiの配布しているパッケージが対話環境でもインストールできるというのは知りませんでした。っていうかbitnamiのページのどこにも書いてないんですが・・・><
wget http://bitnami.org/files/stacks/redmine/1.1.1-0/bitnami-redmine-1.1.1-0-linux-installer.bin
chmod +x bitnami-redmine-1.1.1-0-linux-installer.bin
./bitnami-redmine-1.1.1-0-linux-installer.bin --mode text
でうまくいくようです。

しかしこの方法を使うと勝手にApache, MySQL, Ruby, Subversionをインストールされてしまうみたいなので、きちんと管理したいとなるとちょっと不向きかなと。

2011年2月9日水曜日

UITableView の cell の中に UITextView を配置したとき発生する問題とその対処方法

たとえば Twitter クライアントや SMS クライアントのように、短い文章を入力させるような画面を作りたいとき, Grouped Style な UITableView の cell の中に UITextView を配置して画面を作る事が良くあるかと思います。通常、この作りでほとんど問題はありませんが、特定の条件下で問題が発生することがわかりましたので、回避策をメモしておきます。


■問題

この問題が発生するのは、以下の条件を満たしたときです。
  1. UITableView の cell の中に UITextView を配置する。
  2. UITextView に、縦スクロールが発生するぐらい長い文字列を入力する。
  3. UITextView の中ほどにカーソルを移動する。
  4. UITableView と UITextView の scrollEnabled プロパティを NO に設定する。
  5. UITextView の text プロパティを変更する。
すると, UITextView および UITableView の scrollEnabled プロパティを NO に設定しているにもかかわらず、勝手に UITableView がスクロールを起こしてしまいます。 UITextView の text プロパティを書き換えたら勝手に UITextView が一番下までスクロールしてしまうのはデフォルトの挙動であり、これは問題ありません. UITextView を単品で使っているときは, scrollEnabled プロパティを NO に設定することでスクロールを発生させずに text を書き換えることが可能なのですが, UITextView を UITableView の中に含めたときのみ, scrollEnabled プロパティが無視されてしまい、この問題が発生してしまうようです。

カスタムキーボードを用意したり、本文中のURLを短縮して差し替えたりなど、結構 UITextView の text プロパティを直接書き換えたい要件があったりするので、このままでは困ります。


■解決方法

そこで、相当な荒技ですが、以下のようにして解決を図ることが可能です。
  1. UITableView の cell の中に UITextView を配置する。
  2. UITextView をいったん removeFromSuperview する。
  3. UITextView をいったん UITableView 以外の場所に addSubview する。
    • UIWindow の直下などがおすすめ。
  4. UITextView の scrollEnabled プロパティを NO に設定する。
  5. UITextView の text プロパティを変更する。
  6. UITextView の scrollEnabled プロパティを YES に設定する。
  7. UITextView を元の view に addSubview する。
  8. UITextView を first responder にする。
コードにすると以下のようになります:
UIView *parentView = textView.superview;
[self.view.window addSubview:textView];
textView.scrollEnabled = NO;

textView.text = [currentText stringByReplacingCharactersInRange:selectedRange withString:shrunkenURLString];
textView.selectedRange = NSMakeRange(selectedRange.location + [shrunkenURLString length], 0);

textView.scrollEnabled = YES;
[parentView addSubview:textView];
[textView becomeFirstResponder];
こうすることで, UITableView が勝手にスクロールしてしまう問題を回避することが可能です。

2011年2月4日金曜日

TestFlight 経由のアプリのダウンロード速度を調べてみた




最近 iOS 界隈を賑わせている TestFlight というWebサービスがあります。詳細についてはまぁ TechCrunch さんでも読んでいただくとしまして、この TestFlight が非常に便利そうなので私のほうでも早速試してます。登録も簡単、アプリのマネジメントも良くできていますし、チームを複数用意して管理できるというのも良いです。なんで Apple がデフォルトでこれを用意してくれないのかというぐらい便利です。

が、一つだけ気になったことがあって、まぁタイトル通りなのですが、TestFlight経由だとアプリのダウンロードが遅いのではないか?ということです。ということで実際にやってみました。


■条件

対象のアプリのサイズは17.7MB。
以下の2カ所の回線で計測しました。
  • 会社の回線(確かバッファロー, IEEE 802.11g, だいたい300KB/s程度)
  • 自宅の回線(AirMac Extreme, IEEE 802.11n, だいたい1MB/s程度)

■結果

会社の回線・・・ダウンロードに12分~15分程度
自宅の回線・・・ダウンロードに1分前後、インストール完了まで2分ちょっと

それぞれ別の日に計測したので何とも言えませんし、会社の回線は自分以外の人も使うので遅いときがあるのですが、およそ

遅いとき・・・20KB/s程度
早いとき・・・300KB/s程度

とまぁずいぶん幅がある結果になってしまいました。どうやら時間帯や相手のサーバー側の混雑具合によっても速度が違ってくるようです。いずれにせよそんな猛烈に速いわけではないので注意が必要そうです。たとえば100MBぐらいあるゲームアプリをテストしたいときなどは、今まで通りの地味な配布のほうがよい・・・ということもあるかも。


■余談:TestFlightってどれぐらいのファイルサイズまでアップロードできるのか?

と思って調べてみたらありました。500MBだそうです。400MBのファイルを実際にアップした人がいるみたいです。
http://support.testflightapp.com/discussions/questions/60-max-file-size-on-build-upload

2011年2月2日水曜日

"Failed to launch simulated application: Unknown error." が発生したときの対処法



iPhone シミュレーター起動時に表題のエラーが発生したときの対処法です。


表題のエラーは文字通り原因不明の問題が発生したときに表示されるのですが、問題のうちの一つに「バンドルリソース内にMac OSが使用する予約されたリソース名が含まれていると発生する」というものがあります。

たとえば
Contents
Resources
などの名前のディレクトリやファイルをバンドルリソース内に作成してしまうと、ビルドは通るのですがシミュレーター実行時にエラーが発生するようです。これらの予約された名前は使わないようにすると良いです。

2011年2月1日火曜日

iPhone iPad 向けスタイラスで書き味勝負をしてみた


写真は上から順に、 パワーサポート スマートペン PBJ-9XシリーズPogo SketchoStylus 初期限定生産型


2回戦の記事はこちら: http://akisute.com/2011/05/iphone-ipad-2.html
おすすめスタイラスまとめ記事はこちら: http://akisute.com/2010/06/ipad.html
Bamboo Stylusのレビューはこちら: http://akisute.com/2011/05/ipad-bamboo-stylus.html
oStylusのレビューはこちら: http://akisute.com/2010/10/ipad-ostylus.html

このたび自分が納得できる完成度の iPad 向けスタイラスが3種類揃ったのを機会に、ちょっと書きくらべをしてみる事にしました。


■さっそく試してみた

以下、実際に手書きで字を書いてみたスクリーンショットを貼り付けます。全て一発勝負で書いています。それぞれ上からスマートペン、Pogo Sketch、oStylusです。

neu.Notes



Penultimate




■考察

スマートペンは本当にまるで普通のペンで紙に書いているように書けます。特に「返」のしんにょうあたりを見るとよくわかるのですが、このような綺麗な曲線が非常に楽に書けます。ただしやや太いためか細かい筆跡が難しいです。

Pogo Sketchはスマートペンより細く、やや滑りが悪いため細かい線に強いですし、曲線もしなやかです。しかしながらよく見ると所々線が途切れていたり短かったりすることがわかると思います。これはタッチの感度が悪いためで、タッチを認識せず線が途切れてしまっているためこのようになってしまっています。

oStylusは感度の良い金属製のペン先とペン先が見えるOリング構造のおかげで、細かい字に圧倒的な強さを誇ります。Pogo Sketchのように感度が足りず線が途切れることもありません。弱点として、全体的に長く重くペン先が平たいため、普通のペンと同じようには書けず、線が直線的になってしまっています。

一番書くのが楽だったのはスマートペン、書くのに時間がかかっているのはoStylusだったと思います。


■まとめ

それぞれ一長一短、特徴ある iPad / iPhone 向けのスタイラスが市場に出回るようになってきて、すごくわくわくする感じです!つい1年2年前まではろくな商品がないと嘆いていたのですが、いまや自分の書きたい物に応じてスタイラスを選ぶことさえできるようになりました。素敵!

2011年1月9日日曜日

Python で 5 分でちょっとした XML ファイルを生成する

なんだか西尾先生が面白そうなことをしてるので、便乗してみることにしました。


■課題

今お仕事で iOS のアプリを書いているのですが、その中で次のような plist ファイルを作成する必要が出てきました。 plist ってのをご存じない方は、要するに XML ファイルだと思ってください。
    <dict>
<key>emojiName</key>
<string>表情(嬉しい)</string>
<key>emojiCode</key>
<string>0xE415</string>
</dict>
<dict>
<key>emojiName</key>
<string>表情(にこにこ)</string>
<key>emojiCode</key>
<string>0xE056</string>
</dict>
<dict>
<key>emojiName</key>
<string>表情(笑顔)</string>
<key>emojiCode</key>
<string>0xE057</string>
</dict>
こんな感じで全部で400項目ぐらい。Webページの資料があるので、そこから転載して上記のような plist にするお仕事です。

私がやってもいいのですが、あいにく手が回りません。そこで最近弊社に入社してきました新人さんにお願いすることにしました。ところがどっこい、本当に一個ずつ手で書いているからなかなか作業が進みません。ほかのお仕事もあるのですが、二日でやっと5分の1程度。これはどげんかせんといかんですね。


■さっそくやってみよう

まずは下準備として、ExcelにWebページから情報を貼り付けて整理します。Excelを中間エディタとして使うことで、行と列の操作が簡単になりますし、そのままコピペするだけできれいに表が完成します。整理し終わったら、Excelからテキストエディタにコピペすると、以下のような TSV (Tab Separated Value) になります。
猫 0xE04F
いぬ 0xE052
ねずみ 0xE053
うさぎ 0xE52C
あとはこれを XML にするだけです。さっそくPythonでやってみましょう。
#!/usr/bin/env python

data = u"""
ここに先ほどのTSVをコピペしてください
"""
template = u"""
<dict>
<key>emojiName</key>
<string>%s</string>
<key>emojiCode</key>
<string>%s</string>
</dict>
"""

lines = data.strip().sprit('\n')
for line in lines:
t = line.sprit('\t')
print template % (t[0], t[1])

すごい原始的なコードですが、これでも十分動きますし簡単です。ってあれ、これ元ネタの西尾先生のコードとほとんど同じじゃないですか><


■その結果

まぁそんなこんなで無事 Python のコードもできたので動かしてみましょう。するとあっというまに目的のXMLが画面に出力されました!こうしてなんと残りの5分の4がたったの半日で終わってしまいました!単純な作業は工夫して楽に楽しく素早く片付けてしまいたいですね。

そうそう、ExcelやGoogle Docsのスプレッドシートを一時作業用に使うのはおすすめです。なんだかんだで表を作るならすごく操作しやすいですし、その後作った表をテキストエディタにコピペすれば TSV が簡単に作れるので、便利です。

2010年12月29日水曜日

はじめての iPhone 開発時にお勧めの本 3 冊

2010/12/29追加: 本を追加したり中身を最新のものにしたりしました。

@monjudoh に社内チャットで
[10/09/17 17:18:12] 文 殊堂: heyあきすて
[10/09/17 17:18:32] 文 殊堂: iPhoneアプリ開発でいい本とかある?
[10/09/17 17:19:42] akisute: yoじゅどうもん
[10/09/17 17:19:43] akisute: たくさんあるぜ
[10/09/17 17:19:52] wozozo ☿: yo
[10/09/17 17:19:53] akisute: なんかみんなから同じ質問されるからブログに書くわ
とか言われたので、お勧め本をまとめてみることにしました。2年前にも似たようなものを書いたような気がしますけど、2年が経過していますから、いろいろと状況も変化していますしね。

4797361786詳解 Objective-C 2.0 改訂版
荻原 剛志
ソフトバンククリエイティブ 2010-12-17

by G-Tools


2年前にも詳解しましたがこいつはバイブルです。改訂版になってBlockがらみの記述が増えたり、iOSについての言及が増えて、さらに良書になりました。四の五の言わずに買っておくと良いです。ただし、どちらかというとObjective-Cに焦点を置いている本であり、iOSで何ができるか、ということについてはほとんど記載がないので注意してください。

4797358106はじめてのiPhone3プログラミング
Dave Mark Jeff LaMarche 鮎川 不二雄
ソフトバンククリエイティブ 2009-12-17

by G-Tools


こちらは iOS のバイブルです。初めて挑戦する人に最適ですが、ある程度熟練した人が見ても発見がある良書です。およそ必要なことはすべて得られると思います。

4897978440iPhoneプログラミングUIKit詳解リファレンス
所 友太 京セラコミュニケーションシステム株式会社
リックテレコム 2010-01-12

by G-Tools


UIKitに焦点を置いて書かれている本です。図解が多く、全くiOS開発をしたことがない人でもどのようなUI要素が使えるのか見て学べるため大変おすすめです。

ASIHTTPRequest を使って外部 API から非同期的に結果を取得してみる

ASIHTTPRequest という神の通信ライブラリを使って、うまい具合に外部 API から非同期的に結果を取得・解析して返すようなクラスをつくってみました。以前 NSOperation でやってみたバージョンは こちら。


■主な機能

元々の ASIHTTPRequest にある機能はもちろんご利用いただけます。多すぎて説明し切れませんので、以下の記事を参考にしていただければと思います><

http://d.hatena.ne.jp/ninjinkun/20101122/1290394265
http://macisv.jp/blog/?p=235

さらに今回私が作成した ASIAPIRequest にはこのようなおいしい特典がつきました。
  • POST のパラメータだけではなく、 GET のパラメータも楽々生成してくれるメソッドを用意しました。
  • 非同期実行時の通知方法が, delegate, blockに加え、さらに NSNotification による通知もサポートしました。
  • 非同期的に取得したレスポンスの値をパースするためのコールバックを用意しました。このコールバックメソッドをオーバーライドして、サブクラスで処理を行えば、この中の処理はすべて非同期実行されるため、 XML のパースが遅くて UI が固まったなんてことはもうありません。
  • おまけ的にタグとかつけられるようにしてみました。
その他、お使いになられる際に適当に ASIAPIRequest の中身を書き換え御社のプロジェクトに合うように調整するなどすると面白いと思います。自動的にログインパラメータをつけるようにしたりとか。


■ダウンロード

github にリポジトリを作りましたので、こちらから git でクローンするか、または master のソースコードをダウンロードしてください。

https://github.com/akisute/asi-http-request

タグが付与されていますが、これはクローン元のタグなので、当てにしないでください。常に master の先端をダウンロードするのが一番確実です。

ダウンロードしたら、 Classes ディレクトリと External ディレクトリの中身を適当に自分のプロジェクトにコピーして、プロジェクトに追加していただければOKです。最後に、以下のフレームワークをリンクしてください。
  • CFNetwork
  • SystemConfiguration
  • MobileCoreServices
  • CoreGraphics
  • zlib
ライセンスは元のライブラリに合わせて BSD ライセンスとします。


■使い方

最初に ASIAPIRequest を継承してサブクラスを作成します。
// APIAuthorize.h

@interface APIAuthorize : ASIAPIRequest {
}

// 認証APIのインスタンスを生成する
+ (id)apiWithUserId:(NSString :)userId password:(NSString :)password;

@end
では次にAPIインスタンスを生成するためのクラスメソッドをサブクラスの内部に作ってみましょう。
// APIAuthorize.m

+ (id)apiWithUserId:(NSString :)userId password:(NSString :)password {
NSURL *url = [NSURL URLWithString:@"authorize.json" relativeToURL:API_BASE_URL_STRING];
APIAuthorize *api = [APIAuthorize requestWithURL:url];
api.requestMethod = @"POST";
[api setPostValue:userId forKey:@"userId"];
[api setPostValue:password forKey:@"password"];
api.postRequestFinishedNotificationName = @"APIAuthorizeDidFinishNotification"; // POST 成功時に飛ぶnotificationの名前
api.postRequestFailedNotificationName = @"APIAuthorizeDidFailNotification"; // POST 失敗時に飛ぶnotificationの名前
return api;
}
最後にサブクラス内部でスーパークラスのメソッドをオーバーライドし、通信完了直後に呼び出される処理を記述します。たとえば、レスポンスが返ってきた際に、受け取ったレスポンスをパースして DB に保存したりします。このコールバック内部は UI スレッドとは別のスレッドで並列実行されているので、この中でどれだけ重い処理をしても UI は固まりません。その代わり UI を操作する処理はここでは行わないでください。クラッシュします。
// APIAuthorize.m

- (void)postRequestFinished {
// レスポンスステータスコードが異常系の場合はなにもしない
if (self.responseStatusCode != 200) {
return;
}

// レスポンスをパースしてオブジェクトにし、Core Dataに保存する
// 保存したオブジェクトをuserInfoに格納しておく
User *user = [User managedObjectFromJsonString:[self responseString]
inContext:[AppDelegate appDelegate].managedObjectContext];
[[AppDelegate appDelegate].managedObjectContext save:nil];
self.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
user, @"user",
nil];
}

- (void)postRequestFailedWithError:(NSError *)theError {
// なにもしない
}
tag プロパティを使って、同じAPIでレスポンスの種類を分けることができたりします。
// APIAuthorize.m

enum {
APIAuthorizeResponseTagUser,
APIAuthroizeResponseTagToken,
};

+ (id)apiWithUserId:(NSString :)userId password:(NSString :)password {
NSURL *url = [NSURL URLWithString:@"authorize.json" relativeToURL:API_BASE_URL_STRING];
APIAuthorize *api = [APIAuthorize requestWithURL:url];
api.requestMethod = @"POST";
[api setPostValue:userId forKey:@"userId"];
[api setPostValue:password forKey:@"password"];
api.postRequestFinishedNotificationName = @"APIAuthorizeDidFinishNotification"; // POST 成功時に飛ぶnotificationの名前
api.postRequestFailedNotificationName = @"APIAuthorizeDidFailNotification"; // POST 失敗時に飛ぶnotificationの名前
api.tag = APIAuthorizeResponseTagUser // このAPIのレスポンスはUser型だよーとタグをつけておく
return api;
}

- (void)postRequestFinished {
switch (self.tag) {
case APIAuthorizeResponseTagUser:
// Userオブジェクトを作る
break;
case APIAuthroizeResponseTagToken:
// Tokenオブジェクトを作る
break;
default:
break;
}
}
これで API 本体は完成したので、早速実行してみましょう。以下の4つの方法で実行が可能です。
  1. 同期実行
  2. 非同期実行、 delegate で結果を通知してもらう
  3. 非同期実行、 NSBlock で通信完了後の処理を行う
  4. 非同期実行、 NSNotification で結果を通知してもらう
1, 2, 3 については普通の ASIHTTPRequest と同じですので割愛します。 4 は私が新しく追加した機能で、 NSNotification の仕組みを使って実行完了通知を受け取ることが可能になります。たとえばこんな感じになります。
// 適当に認証とかする画面のViewController.m

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Notification通知を開始する
[[NSNotificationCenter defaultCenter] addObserver:self
name:@"APIAuthorizeDidFinishNotification" // さっき決めた文字列
target:self
action:@selector(apiAuthorizeDidFinish:)
object:nil];
}

- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
// Notification通知をオフにする
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (IBAction)startAuthorize {
ASIAPIRequest *api = [APIAuthorize apiWithUserId:self.userIdField.text password:self.passwordField.text];
[api startAsynchronomus];
}

- (void)apiAuthorizeDidFinish:(NSNotification *)notification {
// 認証完了時の処理
}
この通知はメインスレッドから呼び出されるので、自由に UI を操作することが可能です。

delegate と比べて、 NSNotification を使った通知の便利な点は以下の通り。
  • delegate を使う場合には、 delegate オブジェクトがメモリから消える前に delegate の始末を行う必要があるが、 NSNotification を使った場合にはその必要がない。自分を NSNotificationCenter から削除するだけでよいので、 API リクエストを比較的投げっぱなしにできる。
  • delegate とは違い、複数のオブジェクトで同時に通知を受け取ることができる。たとえば API を実行した画面とは全然違う別の画面二つで同時に通知を受け取ったりすることが可能になる。

■余談

この ASIHTTPRequest は本当にすばらしいです。最初にこの通信ライブラリを知ったときは、また良くあるただのちょっと便利なだけな通信ライブラリなんだろうと思い気にもとめなかったのですが、実際にコードを見てびっくりしました。私がほしかった通信ライブラリそのものだったからです。

私は通信ライブラリは NSOperation を継承して作るべきだと考えており、実際以前に試してみたことがありました。それは主に、
  • NSOperationQueue のシングルトンインスタンスが勝手に通信をすべて管理してくれるので、クラスの変数として通信クラスのインスタンスを保持しておかなくても良くなるかもしれない
  • NSOperation には依存関係を指定するメソッドがあるので、これを用いれば自動的に通信 A, B, C を順序通りに実行するなどできるかもしれない
  • NSOperation を継承すれば、将来 Apple の中の人がフレームワークを改善した際にマルチコア化した iPhone の CPU の恩恵を自動的に受けられるかもしれない
という考えがあったからです。そしたら見事にこの ASIHTTPRequest が NSOperation を継承して、しかも何かと問題の多い NSURLConnection を使わず NSStream とソケットを用いて自分で Run Loop を回すというすばらしい実装をしているじゃないですか!こりゃもうかないません。自分でやる必要が丸でなくなってしまいました。しかもそれだけではなく、
  • 現在全体の何%まで読み込みが完了したかを delegate で通知できる
  • 通信完了時に呼び出される delegate method はすべて main thread から呼び出されるので、 UI 操作をしても安全
という、まさにかゆいところに手が届く良さがすべてあります。惚れる。もうおそらく当分の間はこれ以上の通信ライブラリが現れることはないんじゃないかと言い切って良いぐらいすてきです。

アプリのビルド時に CSSMERR_TP_NOT_TRUSTED エラーが発生したときの対処法

http://d.hatena.ne.jp/drill256/20090820/1250752178
http://discussions.apple.com/thread.jspa?threadID=1630090

このエラーは、以下の証明書がすべて存在しないか、または Keychain Access 内でのステータスが、「この証明書は信頼されています。」ではないときに発生するようです。
  • Apple Worldwide Developer Relations Certification Authority
  • iPhone Developer または iPhone Distribution


上記の画像のように、「この証明書は信頼されています。」と緑色のチェックマーク付きで表示されている必要があります。そうでない場合は何らかの問題があります。

対処法は、
  1. まず何はなくともこれらの証明書がすべて存在するか確認する。 Apple Worldwide Developer Relations Certification Authority を忘れているケースが良くあります。
  2. 「この証明書は信頼されています。」になっていない場合には、証明書を選択して、「情報を見る」 -> 「信頼」 -> 「システムデフォルトを使う」 を選択する。「常に信頼する」ではダメです、エラーになります。
証明書をどのキーチェイン項目に入れていても問題はなさそうです。私の場合は WWDR をシステムに、 iPhone Developer をログインに入れていますが正常に動作しています。

2010年11月16日火曜日

iOS で正規表現を使う (3.0, 3.1, 3.2, 4.0)

iPhone / iPad アプリで正規表現を使いたいときはどうするのか調べてみました。


■iOS 4.0以上

NSRegularExpression があるのでそれを使えば万事解決です。


■iOS 3.2

http://blog.livedoor.jp/pnfhy316/archives/277806.html
こちらのブログでご紹介されているとおり、NSString rangeOfString:options: で NSRegularExpressionSearch を指定するのが楽です。

正規表現のグループ参照はできませんけれど、まぁしょうがないですかね。


■iOS 3.1, 3.0

http://d.hatena.ne.jp/KishikawaKatsumi/20081031/1225463896
こちらのブログでご紹介されているとおり、ライブラリを使うのがよいようです。

または最近調べてわかったのですが、 NSPredicate を以下のように使うと正規表現によるマッチングが可能になる模様です。
NSString *path = @"/path/to/img/img10001.png";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ matches '.*/img[0-9]{5}\\.png'", path];
BOOL matched = [predicate evaluateWithObject:nil];
キモは NSPredicate の format として matches という構文が使えることと、その中で正規表現が使用できるところですかね。実機の iOS 3.X で試したわけではないのですが、 NSPredicate 自体が iOS3.0 から使用可能になっていることと、 NSPredicate format 関連のドキュメントを見ても特に利用制限とかは書いていなかったので、普通に3.0から使えるんじゃないかと思ってます。もしよろしければ動作報告いただけるとうれしいです。


■iOS 2.X

iOS界のIEだと思って忘れましょう。お客さんに「頼むから対応してくれ」と言われても突っぱねる(すでに全体の1%以下ぐらいのはず)のがベストです。

2010年11月4日木曜日

iPhone / iPad でIPアドレスやMACアドレスを取得するクラスを書いてみた

たまには技術ネタを書きます。 iOS で IPアドレスや MACアドレスを取得するためのObjective-Cクラスを書いてみました。
コードはこちら。

https://gist.github.com/662203

ライセンスは記載のとおりMITライセンスです。

参考にしたサイトはこちら。

http://stackoverflow.com/questions/677530/how-can-i-programmatically-get-the-mac-address-of-an-iphone
http://iphonesdksnippets.com/post/2009/09/07/Get-IP-address-of-iPhone.aspx


■ほんのちょっとだけ説明

BSDのioctl()関数を使いますと、デバイスのIO周りのありとあらゆる情報を取ってくる事ができます。・・・らしいです。それを使ってIPアドレスやMACアドレスを取ってみました。大本のコードは確か StackOverflow あたりにあったのですが、もろにC言語ベタベタだったので使いやすくするためObjective-Cのクラスにしています。といってもまだまだ改善点が山ほどありますので好き勝手に触りまくってください。

っていうか今気づいた、二番目のサイトそのまんまほとんど同じコードじゃないですか>< 別にいまさら作らなくても良かったですね。

2010年10月26日火曜日

ゲームプログラマになりたい中学三年生のためにアドバイスを考えてみた

■発端
  1. 先日誕生日だったので実家に電話した
  2. おかんが出てきた
  3. おかんの友人の奥様の息子さんの話になる
    • なんでやねん
    • 中学3年で進学シーズンです
  4. なんかゲームプログラマーとかになりたいらしい
  5. けど情報科の高校とか嫌で工業高校がいいらしい
  6. あんたプログラマーでしょなんかアドバイスとかないの
という無茶振りを受けたのでこんな記事を書く事になりました。 あれ、又このパターン?


■いきなり結論

ゲームプログラマーですか? もちろんなれます。続けられるのであれば。

なんだか世の中には 一万時間の法則 というものがあるらしくて、一万時間ほど経験を積めば大成できるらしいです。私も身に覚えがちょっとだけあるので、この一万時間というのはあながち間違ってないのではないかと思います。リンク先の lifehacking.jp さんの記事によりますと、
2年:10000 / (2 x 365) = 13.7 時間
5年:10000 / (5 x 365) = 5.4 時間
10年:10000 / (10 x 365) = 2.7 時間
らしいので、こう考えることが出来ます。
一日 13.7 時間、365日、一日も休むこと無くPCの前に座ってプログラムを書いていられる!
おめでとう、2年でゲームプログラマーになれます!むしろイチローにだってなれますよ!
一日 8 時間、一日も休むこと無く学校に通ってプログラムの勉強ができる!
3年間学校に通うとすれば、卒業するまで毎日やってもまだ6割程度。残りは自分で身につけるため、まだまだ勉強を続ける必要があります。
一日 2.7 時間、365日、一日も休むこと無く家に帰ってからプログラムを書いて勉強できる!
それでも10年続ける必要があります。いまあなたが15歳なら、25歳です。5歳のときの自分、覚えてますか?
はい、相当大変だということがおわかりになるかと思います。つまりこれだけの大変なことをどうやって毎日続けるか、というのがゲームプログラマーになるキモのようです。早速次章以降で検証してみましょう。


■でもちょっとまった

ただちょっと思ったのが、多分彼はゲームプログラマーという仕事を勘違いされているんじゃないかと。ゲームプログラマー = ゲームを作れる人、と思われているのではないかと。実際私も中学生ぐらいの頃はそうでした。

しかし現実は ゲームプログラマー = ゲームを作れる人 ではなく、 ゲームプログラマー = ゲームの一部を作れる人 です。まずこれに気づいて、この現実を受け入れることがすごく大事です。

これはものすごく当たり前のことなのですが、たいてい子供時代に夢見ているゲームプログラマー像にはこれが含まれていません。実際、面白いゲームを作るには、
  • むちゃくちゃ面白いゲームのアイディア
  • 超カッコイイグラフィック
    • 2D
    • 3D
  • 爽快な音楽
    • BGM
    • 効果音
  • そしてもちろんプログラミング
と、これだけの要素が必要になってしまうわけです。もちろんアイディアも絵も音楽も借りてきたりフリーの素材を使ったりできますが、すべてがすべてフリーの素材でまかなえるわけではなく(そもそもやってみるとわかるのですが、すべてフリーの素材で作ると、自分が作ったものではないように感じられ気が萎えてしまいます)、どこかはオリジナルでやらなくてはなりません。で、当然自分ひとりでこれだけの要素をまかなえるかというと 普通は無理 なわけです。もちろん 例外 もいらっしゃいますが・・・とにかく、 ゲームは自分ひとりで作るものじゃないんです。

で、それではゲームプログラマーがやるプログラミングってなんなの、という話になりますが、一番地味です。どんな名作ゲームでもプログラマーが褒められることはまずありません。考えてみてください:
  • このゲームは話が面白い
  • このゲームはシステムが斬新
  • このゲームはゲームバランスが絶妙で対戦が面白い
  • このゲームはグラフィックが超綺麗
  • このゲームのBGMはマジ燃える
どれもゲームを褒める言葉なのですが、なんとまぁ、どの褒め言葉も全然プログラマーが褒められてません。

じゃあ何が楽しくてゲームプログラマーをやるのか?

最高の自己満足です。プログラムを書くこと自体が楽しいのです。以上。

さて、どうしてわざわざこのような事を書いたのかといいますと、実は彼がなりたいのはゲームプログラマーではなくてゲームに関わる仕事の何かじゃないかと思ったからなのです。単にゲームが好きで、PCを使ってゲームに関わる仕事がしたいなら、いくらでも他に道があります。絵を書いたり、音楽を作ったり、シナリオを書いたり。いろいろ試してみて、一番自分が好きになったものを極めればいいのです。ですので心あたりがあるのであれば、プログラミングと決めつけずに絵を書いたり音楽を作ったりシナリオを書いたり、ああそれより何より大事、自分以外のゲームに関わる仕事がしたい仲間を探してみたり、いろいろやってみてください。どうすればいいかわからない?私はプログラマーであって絵を書いたり音を作る方法は全然知りませんが、 Google先生が知っています。 思いつく限りGoogle先生に100回でも200回でも聞けばいいのです。一日 2.7 時間、365日、一日も休むこと無くプログラムを書くのに比べたら屁でもないぐらいカンタンですよね?


■それじゃあプログラマーになろう

よし、これだけ脅してもまだ プログラマー最高!! とお考えのHENTAI諸君、ようこそ!それではさっそく10年間続ける方法を考えます。
  1. 楽をして楽しんで毎日続ける
  2. ほんの僅かでもわからないことがあったら即ググれ
  3. エライ人と仲良くなれ
  4. 英語は絶対に意地でも覚えろ
  5. 入門書を読む
まず一番目。私が事あるごとに言ってますが、楽をしましょう。自分に一番あった方法を取って楽をして、そのかわり毎日続けましょう。ゲームプログラマーになりたいのだからまずはゲームプログラミングだろうと思われるかもしれませんが、実はゲームプログラミングはプログラミングの最高峰、最も難易度が高いのです。少なくともWebプログラミングや、最近流行りの iOS / Android でのプログラミングに比べれば、格段に難しく、いきなりここから入ると確実に心がバキバキ折れます。自分はガッツがあって、絶対最後までやり遂げてやると決めて最後までできる人でしたら、このいきなりゲームプログラミングから入ってゲームプログラマーになるのが最短ルートですが、私には到底不可能でした。

そこで最初はまず楽しむことをおすすめします。F1レーサーだって、最初からいきなりF1マシンに乗ったりはしません。最初は普通の車やカートに乗ります。同じように、いきなり難易度激高のC言語をマスターするのではなく、楽をして楽しんで毎日続けられてしかもためになる、そんなプログラミング言語がいいですよね。いくつか私のおすすめをご紹介しましょう。
Processing
本当におすすめです。ぶっちゃけこの言語だけで2Dグラフィックを使った簡単なゲームを作ることが出来ます。いちいち超面倒なrun loopの管理を自分でする必要もありません。そして使っていて面白い! http://akisute.com/2009/12/processing.html とか見ていただければ面白さが伝わるかと!Processingで何か色々作ってみて、遅いとか重いとかもっと難しいことがしたいと思ったら、DirectXとかバリバリ使ったものに挑戦すればよいのです。
Python
これは非常に地味な言語なのですが、プログラムのロジックやオブジェクト指向などを学ぶのに最適だと思ってます。なにより使っていて楽です。もっとも楽すぎてC言語に戻れなくなるかもしれませんが・・・
HSP
これも入門にはよさそうなのですが、使ったことがないのですみません、感想が出せなくて。
さて次、二番目。ほんの僅かでもわからないことがあったら即ググれ。自分で調べる力がないと仕事でプログラムなんぞ書いていられません。しかし、自分で調べるのは大変な作業で心が折れるときもありますし、なによりGoogle先生ですらわからないことはたくさんあります。

そこで三番目。エライ人と仲良くなれ。昔はそんなの絶対不可能でしたが、いまや Mixi Twitter はてな Facebook なんでもござれです。これらのサービスを使ってちょっと調べればHENTAIレベルの神プログラマの日々のつぶやきを簡単に見ることができます。テンション上がりますね。それにひょっとしたらHENTAIたちと仲良くなれるかもしれません。そうすればGoogle先生すら教えてくれない情報をお互いに交換し切磋琢磨することができるようになります。

中学三年生でプログラマーといえばやっぱり tefu 君をフォローするのがいいのかなと。

四番目。英語は絶対に意地でも覚えろ。これは英語が好きとか嫌いとか関係なく必須です。覚えてください。なぜかというとあなたの目の前にあるコンピューター自体が基本的には英語のような言語しか理解出来ないから(日本語もついでに表示できるよっていう程度に思っておけばよいかと)と、そのコンピューターの中身を作っている連中のほとんどが英語でしゃべっているからです。当然文章も全部英語です。今後もこの状態が変わることはまずないでしょう。なので意地でも覚えてください。どうしても嫌ならば、最後の手段。海外のゲームを買ってきて辞書片手に英語字幕のままプレイしてください。私はこれで覚えました。

最後、入門書を読みましょう。ぶっちゃけ私なんぞに聞くよりそのほうが何倍もいいです(これが言いたかった!)。でもどれを買えばいいかって?これが難しい。でも基本的にはGoogle先生に聞くか、エライ人に聞くかですよね?ここではひとつだけ私が最も好きな本を紹介しておきます。

4798021180ゲームプログラマになる前に覚えておきたい技術
平山 尚(株式会社セガ)
秀和システム 2008-11-15
by G-Tools

セガの中の人が書いている本なので大変スパルタですが、非常にためになります。


■ゲームプログラマーに必要なスキル?

いままではとっかかりのところを説明しましたが、うってかわって仕事でゲームを作るにはこれぐらいのスキルが必要だろうというところです。私は本業がゲームプログラマーではないのでなんともですが、これぐらいは知っておかないとなーと思ってます。なので以下の項目は 好き嫌いかかわらず覚えなくてはなりません。
  • プログラム言語系
    • C
    • C++
  • 学問系
    • 数学, 特に幾何学, 平面幾何は最低限必要, 3次元以上のものが分かれば嬉しい
      • ベクトル
      • 行列
      • 三角関数
    • 物理, 特に運動物理
    • 英語
C, C++はおそらく当分の間はゲームプログラミングの主流言語であり続けるので、覚えておいて全く損なしです。というか、メモリ管理を自分でできない言語でゲームのコアエンジンをかくのは絶対不可能なはずなので、将来的にもC, C++でなくてもそれと同等程度の言語が使われるはずです。

OpenGLとかDirectXを含めていないのは、そんな低レベルなところを直接自分で触ることはまずないと踏んでいるからです。行列の概念さえ理解できていればゲームエンジンにお任せできます。

あと数学の授業は真面目に受けておいたほうがいいです。どこの学校の、文系でも理系でも何でもいいので、嘘でもいいのでうけておきましょう。どうせ細かいことまで覚えて居られませんが、キーワードだけ覚えておけば後からGoogle先生に聞くことが出来ます。私は数学をサボっていたせいで、この年にして勉強し直しです><


■まとめ

こんだけ書いておいて何なんですが、私ゲームプログラマじゃないんです!(キリッ

いや、 ゲームっぽいもの なら作ったことあるんですよ一応><

あ、そうだ、もし世の中のゲームプログラマになりたい中学三年生ぐらいのお子さんをお持ちのお母さん方が万が一
何かの間違いでこのページをご覧になってらっしゃいましたら、一言だけお願い申し上げます。どうかあたたかい目で見守ってあげてください。やりたいとお子さんがおっしゃられたことをやらせてあげてください。私は中学生高校生の身にもかかわらず家に行きこもってネトゲばっかりしてたり、山口から一人で新幹線に乗って大阪東京までネット友達の元まで遊びに行ってましたが、それでも両親は反対しませんでした。おかげさまでネトゲから英語を無駄に身につけたり東京に交友関係ができて東京で就職する際に助かったりいろいろいいことがありました。
そのかわり彼女がいないんですが今ではものすごく感謝しておりますので、どうかよろしくお願いします。

おしまい。

2010年10月10日日曜日

iPad 用スタイラス oStylus を試してみた



つい最近販売開始した oStylus という全く新しいタイプのスタイラスを購入して二週間ほど使ってみましたので、レビューを書いてみます。

2011/02/01追記 - 新価格など記載


■そもそも oStylus って何?

http://ostylus.com/
http://japanese.engadget.com/2010/07/14/ostylus/

とあるカナダの会社が作った、全金属製の静電誘導式タッチパネル用(早い話が iPad 用)スタイラスです。最大の特徴はこれまでにない全金属製であるということ(なんと先端まで金属です)と、なにより先端のタッチ部分が Oの字 になっていてタッチ面を見ながら描画ができるという点です。2010/10/10現在はまだ早期生産フェーズということで、限定250本のみ販売。1本75ドルでした。限定モデルにはすべて刻印が彫られており、私のスタイラスは058番です。


■最初に結論

良い点は以下の通り
  • 全金属製のためタッチ感度は抜群(ほぼ指と同じ)
  • タッチペンの滑りが非常に良い、滑らせることを前提にした設計
  • ペン先を見ながら書ける唯一のスタイラス
  • スポンジ式のペンに比べてペン先が頑丈で寿命が長い
悪い点は以下の通り
  • 価格が非常に高い(75ドル)
  • 2010/10/10現在まだ量産されていないため簡単には手に入らない
  • 長くて重い、持ち運びには全く向かない
  • ペン先の自由度が1軸しかないため慣れるまで書きづらい、慣れてもちょっとツラい
  • 余りにも滑りが良く、ペンと紙とは違った感覚のため、字が書きづらい
  • 保護用のビニールが貼ってあるが、それでもiPadの表面にダメージが入る可能性がある
それでは個別に見ていきます。


■外見

全金属製で重さは100gちょっとぐらい、太さは一般的な鉛筆と同じぐらい、長さは日本で一般的に出回っているボールペンなどよりちょっと長い感じです。慣れるまで重くて長く感じます。慣れても大きめのため持ち運びには大変不便です。簡単に携帯できる Pogo Sketch と異なり、 iPad と一緒に持って歩くには何らかの工夫が必要になると思います。

先端の針金みたいなところは一件弱そうに見えますが、頑丈に作られている上に素材も丈夫で、そうそう簡単には破損しません。また金属であるためスポンジタイプのスタイラスと違い摩耗がほとんど発生しません。長持ちします。

Oの字部分の裏にはビニールが貼られており、そのおかげで iPad の表面に傷を付けないですみます。また滑りも良いです。野外広告用のビニールだそうで、耐久性も抜群だとか。このビニールは本体とは別に2つ添付されているので、万が一はげてしまっても安心です。

実際に二週間ほど書いてみたところ、目立つようなキズは確かに発生しませんでしたが、表面に貼ってある保護シート(エレコム製)にほんのわずかながら擦り傷のような後が残りました。保護シートをに貼り替えてみたところいまのところそのようなキズは発生しておりません。保護シートの質によってダメージが発生するのかもしれません。保護シート無しで直接本体に触れさせたときのダメージは今のところ不明です。言われなければ気づかないようなレベルですが、それでも気になる方は oStylus の使用を避けた方が無難です。


■動作

動作についてはこちらの公式ビデオを見るのが一番です。


簡単に補足しますと、まず形状が独特であるため、使い慣れるまで非常に苦労します。長くて重いですし、先端は自由度が1軸しかないため Pogo Sketch のペン先に比べてペンと同じように書きづらいです。しかしながらひとたび慣れてしまうと、このスタイラスのメリットがどんどん生きてきます。金属製だけあってタッチ感度は群を抜いてダントツですし、ペン先のすべりも表面のビニールのおかげか非常に良く、まるで抵抗を感じません。スルスル書けます。ペン先が見えるためズームしなくても細かい入り組んだ線が簡単に書けます。デザイナーさんが絵を描くときに使えそうです。


■まとめ

これまで様々な種類のスタイラスを試しましたが、全く新しく他に例がない独特なスタイラスだと思います。書き味も独特です。個人的には手書きで文字を書いたりするときには Pogo Sketch のほうが実際のペンに近い感覚なので好きなのですが、細かい線を引いたりデザインをしたりする人にはこちらの方が大きくて安定し接地面を見ながら描くことができるのでお勧めです。

2010/10/10現在はまだ価格も高く簡単には手に入らないのですが、今後量産が進むかどうか楽しみなアイテムです。


■2010/10/30追記

作者のAndrewさんから次の量産型モデルについてお知らせをいただきました!それによると、次のモデルはなんと半額の$37.50、今の円高を利用すれば3000円とちょっとで買えるようになるそうです!送料は以前かかりますがかなりの進歩だと思います。

また、以前使っていたときに表面にうっすら傷が入ったと書きましたが、新しくシートを貼り替えて一ヶ月ほど使っていますが全く傷が入っていません。使い方の問題か、もしくはシートの品質の問題だったようです。また、次バージョンではさらに傷がつきにくくなるよう先端の加工が見直されるとのことなので、ますます楽しみです。


■2011/02/01追記

http://ostylus.com/order.html から$37.50と送料で購入できるようになっています!私が持っている早期生産モデルから改良も施されているようです。

2010年10月5日火曜日

Android のメモリ管理は大変です

■理想

AndroidってJavaだからメモリ管理なんてしなくてもいいよね!!


なんて思っていた時代が私にもありました・・・


■現実
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 画面が回転した時など、Activityが破棄されるときに呼び出されます
        // すべてのメモリはここで開放します
        // - 特に危険なのが内部クラス(MyWebChromeClientなど)、正しく開放しないとActivityが開放されません
        // - セットしたbackgroundのcallbackもnullにしないと開放が行われません
        // - webViewのdestroy()を忘れると後からGCが走ったときにVMがクラッシュします
        this.webView.stopLoading();
        this.webView.setWebChromeClient(null);
        this.webView.setWebViewClient(null);
        this.unregisterForContextMenu(this.webView);
        this.webView.destroy();
        this.webView = null;

        Drawable backgroundDrawable = this.backgroundViewGroup.getBackground();
        backgroundDrawable.setCallback(null);
        this.backgroundViewGroup.setBackgroundDrawable(null);
        this.backgroundViewGroup = null;
        this.mainViewGroup = null;
        this.anotherViewGroup = null;
    }
これだけやらないと平気でクラッシュします。マジです。しかもGCが走るまでクラッシュしないとかそういう厄介すぎる現象に2回ほど遭遇しました。

WeakReferenceとか使えばいいんでしょうけど面倒なんですよねぇ。


■Google曰く

http://d.hatena.ne.jp/nakamura001/20101002/1286015483

iPhoneより普通に厳しい気がするのは気のせいでしょうか


■結論

AndroidはJavaではないのできちんとメモリ管理をしましょう!

2010年9月12日日曜日

iPhone 開発規約まとめ

あんまり iOS 上での開発規約とか見かけないので、試しに私が今個人/会社で使っている開発規約を公開してみることにしました。


■設計

設計は所謂 MVC と呼ばれる設計モデルを採用します。ただし、厳密な MVC というわけではなく、以下のような区分になっています。
  • Model
    Core Data を使用します。通常 MVC での Model というと業務ロジック等を含めた業務モデル一般すべてを含むのですが、私の場合は特に Core Data の NSManagedObject を Model として扱い、 Model 単体のみで完結するロジックのみを Model に記述します。たとえば、
    • Core Data から対象の Model とその関連 Model 取得
    • Model の新規作成
    • 新規作成時、更新時に自動的に Model のプロパティを更新する
    • Model のプロパティの値を元に幾何学計算をしたり、一時的に使うキャッシュを用意したりする
    などです。実際のコードのサンプル (ニュースサイトのニュースを表す News モデルの実装) はこんな感じになります:
    #import "News.h"

    @implementation News

    @dynamic body;
    @dynamic title;
    @dynamic imageURL;
    @dynamic objectId;
    @dynamic dateCreated;
    @dynamic dateUpdated;
    @dynamic sortOrder;

    @end

    //----------------------------------------------------------------------------------------
    // ここから上は .xcdatamodel ファイルから自動生成されたコードなので触れないようにします。
    //----------------------------------------------------------------------------------------

    #import "AppDelegate.h"

    @implementation News (NSManagedObject)
    - (void)awakeFromInsert {
    [super awakeFromInsert];
    // Insert
    self.dateCreated = [NSDate date];
    }
    - (void)willSave {
    [super willSave];
    // Update
    // DO NOT DO THIS in here because updating self.dateUpdated will call -willSave method recursively until this application crashes!
    // Use NSManagedObjectContextWillSaveNotification / NSManagedObjectContextObjectsDidChangeNotification and watch the time delta of previous change
    // If the time delta is small enough to ignore, do not update dateUpdated property to avoid infinite loop
    //self.dateUpdated = [NSDate date];
    }
    - (void)awakeFromFetch {
    [super awakeFromFetch];
    // Select
    }
    @end

    @implementation News (DBAccessors)
    + (NSArray *)all {
    AppDelegate *appDelegate = [AppDelegate appDelegate];
    NSFetchRequest *request = [appDelegate.managedObjectModel fetchRequestTemplateForName:@"allNews"];

    // Sort by sortOrder, ASC
    NSArray *sortDescriptors = [NSArray arrayWithObjects:
    [[[NSSortDescriptor alloc] initWithKey:@"sortOrder" ascending:YES] autorelease],
    nil];
    [request setSortDescriptors:sortDescriptors];

    NSError *error = nil;
    NSArray *resultArray = nil;
    if (!(resultArray = [appDelegate.managedObjectContext executeFetchRequest:request error:&error])) {
    // handle the error;
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    return nil;
    }
    return resultArray;
    }
    + (id)get:(NSString *)targetId {
    AppDelegate *appDelegate = [AppDelegate appDelegate];
    NSDictionary *substitutionVariables = [NSDictionary dictionaryWithObject:targetId
    forKey:@"objectId"];
    NSFetchRequest *request = [appDelegate.managedObjectModel fetchRequestFromTemplateWithName:@"getNews"
    substitutionVariables:substitutionVariables];

    NSError *error = nil;
    NSArray *resultArray = nil;
    if (!(resultArray = [appDelegate.managedObjectContext executeFetchRequest:request error:&error])) {
    // handle the error;
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    return nil;
    }
    return [resultArray lastObject];
    }
    + (void)deleteAll {
    AppDelegate *appDelegate = [AppDelegate appDelegate];
    NSFetchRequest *request = [appDelegate.managedObjectModel fetchRequestTemplateForName:@"allNews"];

    NSError *error = nil;
    NSArray *resultArray = nil;
    if (!(resultArray = [appDelegate.managedObjectContext executeFetchRequest:request error:&error])) {
    // handle the error;
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    return;
    }

    // Delete all objects fetched
    for (NSManagedObject *obj in resultArray) {
    [appDelegate.managedObjectContext deleteObject:obj];
    }
    }
    @end
  • Operation
    本来の MVC モデルには存在しませんが、私は特別に Operation という区分をもうけています。定義は以下の通り。
    • 一つの業務モデル、業務ロジックを完結させるひとまとまりのロジックであること。要するに本来の MVC モデルの業務ロジック的要素であること。 もっと言うなら Operation だけを切り出して単体テストできること。
    • それなりに重要でかつ量があり、 Controller や Model から共有的に呼びだされること。
    • 非同期で処理ができること。
    • NSOperation クラスのサブクラスにすること。
    たとえばインターネット経由で API を実行して結果を Model に突っ込む処理などは Operation として実装しています。 NSOperation として実装することで、非同期処理が簡単にできるようになるだけではなく、 タスクの依存関係を決めて先に親タスクが実行されるのを待つようにしたり、現在の状態を厳密に判定したり、タスクをキャンセルしたりするのが容易になります。また、将来的に iPhone / iPad のCPUがマルチコアになった場合、 NSOperation を継承しておけばほとんど何もしなくてもその恩恵を受けることができるはずです。
  • View
    ビューです。主に UIKit および Three20 を用いて構築し、必要に応じてその他のビューライブラリを組み込みます。個人的には UIView のサブクラス一般をすべて View として扱っています。 View には描画ロジックと幾何学計算などの補助ロジック、およびそれに必要な最小限のデータのみを持たせるようにします。状態を持ってもかまいませんが、状態のコントロールを View 自身が行うのはできる限り避けます。タッチイベントのハンドリングは必要に応じて View で touchesBegin: などを実装して行いますが、基本的には Controller 側で UIGestureRecognizer を用いて行います。
  • Controller
    コントローラーです。私の中での定義は UIViewController のサブクラスです。アプリの内容に応じて UIKit と Three20 を使い分けます。 本来 Controller は全体の流れのみを管理し実際の処理を行ってはならないということになっていますが、 UIViewController の性質を鑑みて、私は次のように Controller の役割を定義しています。
    • View の管理。
    • 画面遷移, 要するに Controller 間の遷移の管理。
    • タッチイベント、ジェスチャイベント、加速度計などからの入力の受付と処理。
    • 各種 UI コンポーネントの Delegate 処理。 UITextView, UIPopoverController, UIActionSheet, などなどなど多岐にわたります。
    • Operation 完了時の処理など、各種 Notification を受け付けて処理。
    • 上記に必要になるデータ/メソッドの定義。 View に持たせるぐらいならこちらに持たせる。他に持たせるところがないなら Controller にすべて持たせる。
    ということで、 Controller を名乗っていますが実際にはかなり自分で処理をします。こんなのいちいち分けていたら大変ですし・・・といういいわけです。実際のコードサンプルはお見せできないのですが、実装の概要はこんな感じです



  • Application Delegate
    これも本来の MVC モデルには存在せず、本来は Controller として扱うべき物だと思いますが、私は特別に分けています。 Application Delegate は所謂アプリケーショングローバルなデータや設定、オブジェクト、ユーティリティメソッドを管理するものとして扱います。たとえば、
    • Operation 駆動用の NSOperationQueue
    • Model のための NSManagedObjectContext / NSManagedObjectModel / NSPersistentCoordinator
    • 現在通信可能か否かを判定するためのユーティリティメソッド
    • アプリケーショングローバルな Notification を受け取って Application Delegate のプロパティとしてセットする
    などです。これらはほぼすべてのアプリで絶対に必要になるのでテンプレ化しています。


■Xcodeプロジェクト

プロジェクトのグループはこんな感じで分けます。



ビルド設定については、プロジェクト全体のビルド設定は可能な限り使わないようにして、ターゲットごとのビルド設定 ( Cmd+Option+E ) を主に使います。ターゲットが例え複数に分かれても常に同じ物を使うところはプロジェクト全体の設定を変更します。


■命名規則

大体以下のような方針でやっています。
  • Foundation や UIKit など Apple の使っている命名規則に似せること。似てないシグネチャは即リファクタリング対象。
  • ミススペル、Typo、意味のわからない英単語 ( registとか ) は一文字一単語であろうとも発見した瞬間即リファクタリング対象。英語辞書のソースは http://www.alc.co.jp/ とする。
  • 名前はどれだけ長くなろうとも絶対に省略しないこと。 updUniqUsr とか発見したら即リファクタリング対象。逆に BPSuperDuperViewControllerDidFinishedUberTaskNotification とかは表彰モノ。
    • これを BPSDVCDFUTN とか略した奴が現れたら即 SATSUGAI モノです


■まとめ

ここまで書いておいて何ですが、要するに 一貫した基準があるなら 自分らの好きなようにするのが一番いい のかなと思います。

idea mapper for iPad が有料ランキング一位になりました



弊社ビープラウドが開発を担当しました idea mapper for iPad がなんと日本の App Store で有料ランキング一位を取ってしまいましたので、記念POST。 Good Reader や i文庫HD より上ってマジですか・・・>< ほんと企画とデザインをされたサイテックさんのおかげだと思います。ありがとうございます。

ところで気になったのがランキングの推移の仕方。このアプリ、実は日本の有名どころのレビューサイトに全然レビューして貰ってないんです。少なくとも私が自分で観測した範囲では、2010/09/12現在 AppBankさんにもapptoiさんにもラボさんにもお宝鑑定団さんにも掲載されていなくて。実際初日は仕事効率化カテゴリ30位ぐらいだったのに、一週間ぐらいかけてじわじわ伸ばしてきて今の順位に。

これは個人的にはかなり衝撃でした。というのもことあるごとに聞いていたiPhoneアプリのセールス/マーケティングの基礎が、「大手のレビューサイトにレビューを載せて貰う」ことだったからです。ではどこからこれだけ人が流入したのか?と考えてみたところ、サイテックの社長さんが凄く積極的にTwitterで宣伝してくださって、口コミでどんどん広がっていき、ランキングに載ってそのまま加速度的にダウンロードが増えたのではないかなと思ってます。Twitterの力は偉大と考えるべきなのか、日本の App Store の規模が小さいおかげで助かったと考えるべきなのか、どっちなのかは分かりませんが・・・何かの参考になれば幸いです。

2010年9月5日日曜日

Three20 の TTURLRequest は POST メソッドのリクエストもキャッシュしてしまう

Three20 には TTURLRequest という Three20 フレームワークの通信関連を一手に引き受けている通信用クラスがあります。基本的には NSURLConnection クラスとほぼ同等なのですが、リクエストを作ったりレスポンスを delegate でハンドルするのがより簡単になるように作られていたり、独自のキャッシュを使用してより効率的なキャッシュをするようになっていたり、様々な点で NSURLConnection より優れていて便利に使えます。

ですがいくつかハマリどころもありまして、今回はそれを紹介します。ちなみに今回使用した Three20 のバージョンは http://github.com/facebook/three20 の default ブランチの 2010/09/05 付での最新コミットです。今後修正される可能性があります。

実は TTURLRequest は、デフォルトでは HTTP メソッドの種類に関係なく、一律すべてのURLをキャッシュするようになってしまっています。そのため、キャッシュ設定をせずに POST メソッドを使って Web API を実行したり、 RESTful な Webアプリ に PUT や DELETE を送ってしまうと、二回目以降のリクエストがキャッシュされてしまいサーバーにリクエストが飛ばなくなってしまいます。回避方法は以下のどちらかを使うと良いです。
  1. 手動でリクエストを作成する際にキャッシュ設定を明示的に指定する
  2. フレームワーク側を書き換えてしまい、 POST, PUT, DELETE 実行時にはキャッシュを無視するようにする

■手動でリクエストを作成する際にキャッシュ設定を明示的に指定する
一番簡単です。以下のようにして明示的にキャッシュを使わないように指定します。
TTURLRequest *request = [TTURLRequest requestWithURL:@"http://mypage.example.com/api/something/post" delegate:self];
request.httpMethod = @"POST";
request.cachePolicy = TTURLRequestCachePolicyNone;

■フレームワーク側を書き換える
でも個人的には GET 以外でリクエスト結果がキャッシュされるのは誰がなんと言おうと不具合だと思っているので、 TTURLRequestQueue.mloadRequestFromCache: メソッドを以下のように書き換えて対応しました。
- (BOOL)loadRequestFromCache:(TTURLRequest*)request {
if (!request.cacheKey) {
request.cacheKey = [[TTURLCache sharedCache] keyForURL:request.urlPath];
}

if (IS_MASK_SET(request.cachePolicy, TTURLRequestCachePolicyEtag)) {
// Etags always make the request. The request headers will then include the etag.
// - If there is new data, server returns 200 with data.
// - Otherwise, returns a 304, with empty request body.
return NO;
//-----------------------------------------------------------------------
// ここから下が変更点
//-----------------------------------------------------------------------
} else if ([request.httpMethod isEqualToString:@"POST"] || [request.httpMethod isEqualToString:@"PUT"] || [request.httpMethod isEqualToString:@"DELETE"]) {
// HTTP POST/PUT/DELETE should not use cache.
// Only HTTP GET can use this cache.
return NO;
//-----------------------------------------------------------------------
// ここまでが変更点
//-----------------------------------------------------------------------
} else if (request.cachePolicy & (TTURLRequestCachePolicyDisk|TTURLRequestCachePolicyMemory)) {
これですべてのHTTPリクエストにおいて、 POST, PUT, DELETE 時にキャッシュが使われなくなります。

2010年8月29日日曜日

自分なりの iPhone アプリ開発手法とかこだわりとか書いてみた

Twitter で vの人こと @voluntas さんに されたので、自分なりのポリシーとかこだわりとか開発手法とかをまとめてみることにしました。今仕事で iPhone アプリの開発を主にやっているので、 iPhone アプリに関する内容が多いですが、それ以外の開発でも使えると思います。

あまり技術的な内容やツールに関する内容はありません。それらは別エントリーにまとめようと思います。


■大前提: 自分を知る
まず何はなくともこっからです。なんだか開発とか全然関係ないじゃないか、怪しい自己啓発じゃねえかと思われるかもしれませんが、敵を知り己をを知れば百戦危うからずと昔のエライ人も言ってます。それにそもそも私がどのような人間なのかを理解しないと、せっかくの開発手法もそのまま真似してはうまく合わない・上手く回らない・賛成できないということになりますので、非常に大事だと思います。ということでさっそくまとめてみました。
  • 恐ろしく几帳面である
    A型人間です。
    Java とか大好きです。逆にカオスなコードが苦手で、 PHP とかは敵です。
    掃除してない @feiz@shin_no_suke の机とか見てると勝手にゴミを捨てたり掃除したくなります。
    Typo とか見つけたら人のコードでも勝手に直します。 Typo するのが嫌なので毎回変数名を決める前に英語辞書を引きます。
  • ゆとりである
    一日の労働時間は八時間までよねー(キリッ
    なのでとにかく楽をしたがりです。
    そもそも体力が小学生並なので徹夜仕事とかできません。そのような事態に陥らないような仕事の進め方が必要になります。
  • やたらこだわる
    好きなモノは徹底的に好きで嫌いなモノは徹底的に嫌いです。なので評価が極端で言動が偉そうです。すみません><
    一歩間違えるとただの頑固者になるので気をつけています。
  • 言動は偉そうだけれども中身は非常に臆病者で弱い
    なのでゆとりを失うと一気に迷走します。余裕を常に持てるようにする必要があります。
    また周りの皆さんの支援ないと生きていけないと思っていますので、「生意気だけれどもイイ奴」ぐらいのポジションに落ち着きたいと思ってます。
  • iPhone は俺の嫁、 Cocoa Touch は神環境
    もう二度と iPhone の無い世界には戻れません。
    10年後ぐらいにはまた別の面白いものがあると思いますが、今のところは iPhone が一番面白いです。
似たような方は私と同じやり方を真似してもいいし、真逆な方は私のやり方と真反対をやればうまくいくのではないかと思います。


■三つの大戦略
私がどういう人間か分かったところで、次は今の仕事のやり方に関して最も根底にある三つの大戦略をまとめてみました。この大戦略にはほとんどツールや言語の話が登場しません。それらは時代が変われば変化してしまうからです。それよりもむしろ、どういう考え方やポリシーで仕事をすれば最も開発生産性が上がるか、というところに着目しています。
  • 徹底的に几帳面にやる
  • 楽をする・楽しむ・楽をさせる・楽しませる
  • 二度とガラケーには戻らないと決心しその通りに行動する
徹底的に几帳面にやる
几帳面なのが武器なので、それを徹底的に生かす方針で開発を進めます。また几帳面さは Objective-C と非常に相性がよいです。たとえばメモリリークが発生してから調査すると非常に大変ですが、発生する前に防ぐことができれば生産性がそれだけ高まるはずです。また KVC/KVO や Core Data など動的言語のような処理を行う箇所では一文字のスペルミスが発見しづらいバグにつながるため、スペルミスチェックを行ったり、命名規則の策定をしたりする必要がありますが、このとき几帳面さが効果的に働きます。
また、余りつまらないところでミスを連発するとテンションも下がるしリズムも崩れ、余裕が無くなってしまい結果として悪いモノができあがってしまうので、それを防ぐためにも几帳面にやるのが効果的だと思ってます。
なにより、これが一番大事だと思うのですが、几帳面にコードを書くのが自分にとって一番楽しく、モチベーションが上がります。つまり逆に言えば、リズミカルにどんどん細かいところを気にしないでガーッとたくさん作るほうが楽しくリズムに乗れてモチベーションも上がる人でしたら、そのようにする方が良いと思います。

楽をする・楽しむ・楽をさせる・楽しませる
四楽運動です(何それ
要するに、
自分が楽したい、仕事量は減らして簡単にしたい
仕事は楽しんでやりたい、面白い技術を使ってやりたい
でも自分ばっかり楽したら申し訳ないし楽した分だけしっかりやって楽をさせてあげよう
それにせっかく作るなら楽しいモノを作った方がいいに決まってるからそうしよ
ということです。
この方針に従うと、私の場合、楽ができて楽しいだけではなく、「このようなすばらしい環境で開発させて貰ってありがとう」という感謝の気持ちと「それなりの成果を出さなくてはならない」という責任感が自然と発生するため、非常に開発効率が高まるようです。

二度とガラケーには戻らないと決心しその通りに行動する
うちの会社でもガラケー向けのWebアプリの開発をやっていて、その際の苦労話を聞く機会があります。また、企画やお仕事の提案などでGREEやモバゲーなどのガラケーアプリを見せて貰うことがあります。そのような経験から思うのは、
ガラケーはIE6とか比較にならないほど苦労するくせにどうしようもないユーザー体験しか提案できない最悪の開発環境
だということです。たとえばメールの文字コードの扱いとか、特定の機種だけ動かないとかで50台以上の実機でテストしないと駄目とか、Flash lite 1.1はまともに使えないからサーバー側でバイナリ操作してSWFを生成して送り返しているとか、そんなネタを聞くたびにこう、ふつふつとしたものが沸いてきます。
その上お金になるからかバッドノウハウだからか知りませんが、驚くほど情報が出回っていない。調べてもなかなか分からない。とにかく開発者に優しくありません。なによりバッドノウハウを運用しても楽しくない上にイライラして嫌なムードになってしまいます。
確かにガラケーの環境はお客さんからお金を取りやすく儲かりやすいため、会社で取り組むには非常に好都合であるとは思うのですが、やはり私は開発者の身である以上、素直に開発者に優しく、さらには素晴らしいユーザー体験を提供できる環境を選びたいのです。さらに思うだけでは駄目で、実際に iPhone の開発で仕事を取って継続的にお金を稼げるようにしなくてはならないので、そうなれるよう実際に行動する必要があります。


■「徹底的に几帳面にやる」を達成するための戦略
  • 面倒くさがらずにリファクタリングする
    リファクタリングは大事です。納得いくまでリファクタリングします。一文字Typoしたら名前変更、名前が後から気に入らなくなったら変更、クラスの構造を変更、メソッドをパブリックに変更、エトセトラエトセトラ。リファクタリングは他の人がコードを読んだ時の理解を助けてくれるだけではなく、自分自身がリファクタリングのためにコードを理解することになり、今後の設計に役立ちます。自分の考えを整理するという意味でも大事だと思います。
  • 細かくスタートして、ちょっと作って確認し、またちょっと作って確認して繰り返す
    特に iPhone での開発は小規模で変更や追加が多く、アップデートも頻繁。必然的にアジャイルな開発が要求されます。そしてなにより、テストケースを書いてテストドリブンで開発するだけの時間的余裕がありません。そんな贅沢なやり方ができるのは長期でずーっとやっていくWebサービスや大規模なシステムだけで、作ってすぐ放棄される事が多い iPhone の開発にはテストドリブンは全く向いていないと思っています。
    しかしながら、品質は当然担保しなければなりません。そこで私は小さく作ってすぐに作った箇所を確認し、また少し作って確認・・・という手法をとっています。正しく作る単位をモジュール化することができれば、一端テストしてその部分の品質が保証されればその後の開発ではテスト済みのモジュール内の事は考えなくて済むからです。テスト自体が大変でコストがかかるので、やはりコーディングの段階で几帳面にバグを出さないようにするというのが一番に思えます。
  • 一番面倒で難しそうなところからスタートする
    ほとんどの技術者の方はこのような手法を取られていると思うのでわざわざ書くほどのものでもないのですが、それでも一番面倒で難しそうなところからスタートする手法は非常に有効です。上記の手法とも合わせ、一番難しそうなところ一番最初に小さくスタートしてすぐにテストし、品質を保証してしまうのが最も有効です。全体の2割のコードに全体の5割ぐらいの時間をかけて作り、残りの半分はコピペしてプロパティをちょっと書き換えるだけでざざーっと作れるようなところにしてしまう、というのが理想だと思ってます。
■「楽をする・楽しむ・楽をさせる・楽しませる」を達成するための戦略
  • 取捨選択をする
    ソフトウェア開発で楽をするための一番よい方法は、作らないことです。取捨選択を適切に行ない、不要な枝を切り捨て、必要な枝・競争力のある枝にすべてのエネルギーを集約することで、個性的なアプリを作り出すことが出来ます。個性的でないアプリは供給過多の市場に埋没します。さらに取捨選択は作業をすすめる最中でも大いに役立ちます。たまにお客さんから「あれもなる早、これもなる早、全部最優先で」とかいうお前日本語間違ってるだろといわんばかりの指示が飛んできたりしますが、これは全く話になりません。「最たるもの」とは常にひとつしか存在しないもので、だからこそまずそれから仕事にとりかかることができ、余計な仕事の順序選択思考を避けることができるのです。私自身が優先順位をつけたり指示を出したりする際にも、最優先がひとつ、次に優先がふたつ、それ以下はすべて後回しか切り落としです。そもそも、三つよりたくさんを記憶して同時に処理できる人はまれです。
  • 偉い人の作ったフレームワークとかライブラリをどんどん使わせていただく
    ソフトウェア開発で楽をするための二番目に良い方法は、すでに完成しているものを取り込むことです。ということで、フレームワークやライブラリなどは積極的に活用します。ライセンスには気をつけないと大変なことになりますので注意ですが、これらのフレームワークやライブラリは私よりも遥かにすごい人達が時間と手間をかけて作り上げられ、テストもしっかり行った上で公開されているものが多いので、私自身が実装するよりも遥かに信頼できると思っています。もちろん人が作ったものなのでバグや合わない仕様はたくさんありますが、ソースコードを読んで修正すればよいのです。ゼロから自分が作るよりは断然良いです。
  • とにかく自動化する
    とかく人間というのは、単純作業をミスすることにかけては超一流です。頻度の差こそあれ誰しも必ず単純作業をミスします。さらに単純作業は集中力という貴重なリソースを消費します。
    二回以上繰り返す単調作業は自動化する。それがたとえ、ソースコードをzipで圧縮してメールでお客さんに送るといった簡単な作業であったとしても、自動化しておくのは非常に役に立ちます。単純作業を避けることで生まれた余剰集中力を、別のところに振り分けることができます。
    自動化する手法がわからなかったら調べて勉強します。ここでちょっとわからなくて面倒だからと避けると後から地獄を見ますし、成長できないので、意地でも調べます。
  • 自分に要求されている責任範囲以上の仕事を引き受けて楽をさせてあげる
    私は最初の会社の新人教育の際に、「自分が要求されている以上の仕事をしろ」と繰り返し教えられました。実際最初の会社はどちらかというとブラックに属する部類だと思いますし、こういう教えは社畜だの何だのと言われているようですが、私はこの「自分が要求されている以上の仕事をしろ」という考えが好きです。
    確かに前の会社で仕事をしている間はこの考えは嫌いでした。なぜなら仕事がつまらない上に余計なことをすると怒られる職場だったからです。バグだらけの共通ライブラリを修正したら余計なことをするなと怒られ、酷い出来栄えのJavaScriptを救済するためにjQueryを突っ込んでみたらこれまた余計なことをするなと言われ。まったくどうしろと・・・。
    しかし今は違います。仕事の内容は全部任せていただいていますし、効率化も推奨されています。画像リソースがお客さんからやってこなければ自分で作り、画面の仕様が決まらなければ私が決めて作ってお渡しし、セールスのためにアプリレビューを行ってくれるブログ一覧をまとめてお渡しするなど、とにかく仕事が円滑に進むと思えばなんでもやります。面倒なときもありますが、それより相手の仕事が遅くってイライラして「あいつが悪い、あいつのせいでプロジェクトが失敗した」とか考える方が嫌です。それにちょっと勝手にやりすぎたかと思っても、意外なほど喜んでもらえます。
    ただし、何でもかんでもこの方法で一人で引き受けていると私の仕事量が破綻するので、この方法で引き受けるのは一過性の仕事だけにしています。定期的にやらなくてはいけない仕事は自動化するか、やり方を教えてあげてやってもらいます。
  • 細部にこだわれるだけのスケジュール上の余裕を常に持つ
    リファクタリングしたり、自分に要求されている範囲以上の仕事をすると、どうしても当初見積りより遥かにたくさんの時間がかかることになります。それにプロジェクトには絶対にトラブルが付きものです。ということで、時間的なゆとりが必要です。私は見積りを提出する際に、自分で余裕を持ってできる見積りに、さらに1.5をかけて提出したりします。提出するときにはこれ時間がかかりすぎじゃないかと言われないかヒヤヒヤしますが、バグを取ったりクオリティを上げたり、突然湧いたトラブルに対処しているうちに、最終的にはそれで大体丁度良くなるので不思議です。お客さんにとっても納期遅延が発生しないため、今のところ大変ご満足頂いております。
    特に iPhone アプリはこの余裕を持ってアプリのクオリティを上げる方針が有効に働きやすい気がします。修正が比較的簡単ですぐに行えるWebアプリに比べ、リリースを急いだとしても、ひとたびバグが原因でリジェクトされるとリリースが一週間遅れ、醜いアプリをリリースするとレビューで酷評されと、ろくなことがありません。
■「二度とガラケーには戻らないと決心しその通りに行動する」を達成するための戦略
  • ガラケーの仕事は意地でも断る
    「ガラケーは嫌いだけれど、仕事だからしょうがないし・・・」冗談じゃありません。幸いにして iPhone 開発で人材募集をしている会社さんも増えましたし、フリーで出来るお仕事の量も増えています。口だけでなくて行動でやらなければなりませんので、まずガラケーの仕事をお断りです。
  • せっかく無理言って iPhone のお仕事をやらせてもらっているので、とにかく全力でやる
    とまぁこんな具合で無理わがままを通しまして何とか iPhone のお仕事を頂きましたので、その分きっちりお仕事して成果を出して次があるようにしなければなりません。こうして後ろを断つとやっぱりやる気が出ます。また、損益にも気が向くようになります。なにせ赤字垂れ流しではあっという間にガラケーに逆戻りです。利益を出さなくては話になりません。
  • 自分に要求されている責任範囲以上の仕事をやる
    一番信用できて一番自分の思い通りに動くのは自分です。まず自分がやることで成功が近づくと考えています。と同時に一番信用できないのも自分なので、人におまかせしたり既存のライブラリなどを活用したりというのも必要になります。なんかすごい矛盾してますが、とにかく自分が一番信用できるけど一番信用できないのです。
  • 次の仕事を持ってきてもらえるように売上とかまで気にする
    「開発は物つくるまでが仕事、売るのは営業の仕事・・・」ガラケーでやってください。 iPhone 開発者の人は少人数かフリーの人が多いため、みなさん作ったものを売るところまで考えてらっしゃるようです。ただし、あくまで開発者の立場として売上を気にすることです。本職の営業や企画の方がいらっしゃるときに、私が営業や企画の真似事を始めると破綻します。
  • 以上のようなわがままを聞いてくれる環境に身を置く
    「んなもんお前に言われなくてもわかってるわ!そんな理想的な環境あるわけねえ!」すみません、ごもっともです><
    結局良い環境で仕事するのが一番で、良い環境は良い人脈と人付き合いがあれば勝手にやってくるみたいなので、まずは勉強会とかに参加しまして良い人付き合いをするのが一番の近道かなぁと思います。
  • 成果はきっちりアピールする
    成果はアピールしないと次につながりませんので、きちんとアピールします。幸いにして私は口から先に生まれてきた人間なので、大声で騒ぐのは得意です!しかしアピールというのはやり方を間違えると単なるスパムや嫌がらせに成り下がってしまいます。TPOをわきまえて正しく成果を大声でアピールしましょう。
    たとえば @iphone_dev_jp に「新作アプリをリリースしました!」なんて大声で流してもイラッとされるばかりですが、リリースして培った経験や技術情報、お役立ち情報を流せば「あいつはできる」と思ってもらえるわけです。
■チームで作業する際の戦略
ここまでは主に自分自身に関する内容の戦略でした。が、実際の仕事はもちろん自分ひとりだけではなく複数の関係者やチームのみなさんと進めることになります。ということで最後にチームで作業する際に私の考える戦略をまとめてみました。
  • 大前提: 人数は少なければ少ないほど良い 能力は高ければ高いほど良い 距離は近ければ近いほど良い
    これが大前提です。とにかくコストを最小限に保ちつつ、時間あたりの開発能力を最大にする必要があります。大規模開発等では当てはまらないかもしれませんが、仕事の総量が比較的少なめで予算が少なく納期が極めて短い iPhone 開発では、開発・デザイナーあわせて殆どの場合3人以内で足ります。その分各個人の責任範囲が非常に広くなるため、すべてのチームメンバーにそれらの責任を全うできるだけのスキルが必要です。誰しも限界があるので最終的には人に聞いたり頼ったりすることになると思うのですが、どこまで個々人が自分自身で調べて解決できるかというところが大事だと思います。何かあったらすぐ頼るような状態では生産性が落ちます。
    「距離は近ければ近いほど良い」というのは物理的にも精神的にもです。物理的に近いほうがやりやすいというのは、極端な例で言えばお客さんが隣のフロアにいるとかです。何か困ったことが発生したときに、メールのやり取りや電話口でのやりとりで解決するより、走って行ってその場で手取り足取り説明したほうが断然早いしうまくいきます。精神的に近いほうがやりやすいというのは、仲がいい方がチームのムードも良くなるし対立が発生しづらいからです。個人的には、良いチームにはデルタフォースとかSASのような特殊部隊のイメージがあります。あのへんの特殊部隊の人が語るチームマネジメント本とか講演会とかあればいいなぁとか思います。
  • 少数・精鋭・見知った仲でチームを結成する
    • 少数になるようにする - 必要最小限の人以外を入れない
    • 精鋭になるようにする - 可能であればそうするのがよいが、できる範囲でやるのであれば、同じ程度の実力の人を集めてチームにする
    • 見知った仲になるようにする - まず知り合う、勉強会とかに行く、一緒に遊ぶ、共通の趣味嗜好を持つ、距離の近いお客さんを選ぶ
    人を増やすのは比較的簡単ですが、減らすのは恐ろしく難しいです。人が増えると責任が薄まります。「俺がいなくてもどうにかなる」と気づいてしまうとヤル気が極端に下がりますし楽しくありません。もちろん、人が足りなくて毎日徹夜しないと追いつかないような状態になっているのはダメで、バランスが重要ですが、基本ほんの少しだけ足りない側に倒すほうが良いかと思ってます。
    チーム全体が精鋭になるようにするというのは、できるのであればそれが最も素晴らしいことだと思いますが、まぁ現実的に考えて難しいと思います。そんな手段があれば私がまず知りたいです>< で、現実的な作戦としては、チーム全体の実力をできる限り均一化すると言うことが挙げられます。チームメンバーの実力にほとんど差がない状態が、一番チームメンバー個々の能力を最大限に発揮させ、責任感を最大限に発揮させることができると思うからです。一人だけ精鋭がいてもその人に負荷と責任が集中し、他の人はアイツがいるからと思ってしまいます。逆に一人だけおちこぼれるとみんながその一人のせいにして責任を放棄します。
    見知った仲になるようにするために、例えば社内で月一ピザでも食べながら勉強会をやるとか、 誰か の家に飲みに行くとか、お昼ごはんを一緒に食べるとか。ここが一番難しい気がしますが、一番大事なところだと思います。仲がよければちょっとやそっとのミスや負荷があっても許してあげられるようになります。逆に険悪になるとほんの少しのミスや負荷が許せなくなり、さらに事態が悪化します。
    ただし、仲が良いのと馴れ合うのは絶対に違うと思います。馴れ合い始めると新しい人が入ってこれなくなり、仕事に張りがなくなります。これもバランスですね。
  • 開発チームとステークスホルダーが直接コミュニケーションできるようにする
    いくつかプロジェクトをやって気づいたことがこれで、開発チームとステークスホルダー(最終決定権保持者、各種画像リソースなどのリソース提供者)の距離が遠いとプロジェクトが失敗しやすくなります。距離が遠いというのは、間に仲介役 - たとえば営業の人だとか - が入るということです。人ほど信用ならない情報伝送経路はありません。必ず情報の劣化・嘘の混入・膨大な遅延が発生します。このような伝送経路を使うのをやめて、直接ステークスホルダーの人とSkypeでやり取りできるようになると、効率が格段によくなります。ミスも発生しません。
  • それでもどうしても何ともならないとき
    • 人数が増えてしまった - チーム/責任範囲を分割して可能な限り少数にする、または別の仕事を取ってきてそちらにチームを分割する
    • スキルが低い人と一緒に仕事をする必要がある - 低い人の理解に全員が合わせる。
    • 顔も名前も知らない人がチームに入ってきた、どうしてもウマが合わない人がいる - そこでどう仲良くなるかというのが本当にカッコイイ大人のやることではないか(キリッ
    • 物理的に距離が遠い - ツールを最大限に活用する。ツールを導入できるように先方をうまく説得する。
    • ステークスホルダーと直接話しにくい - 無理矢理にでも直接話すのを試みる
    すみません、かなり無茶苦茶です>< というのも私自身ここに書かれているような事態にうまく対処できないからです。実際これらの問題をうまく乗り切られる人は、人間力があるといいますか、磨かれた大人であるといいますか、優れたマネージャーさんであると思います。現実問題、こういった問題は数限りなく存在しますので、具体的な対処策は私自身学び取りたいところです。ですが、絶対に勘違いしてはならないのが、これらはあくまで「対処療法」であって、「根本的な治療」ではないということです。最初から対処療法が必要でないような環境を作るために尽力し、そのような環境で仕事をしていれば、これらの問題はそもそも発生せず、そのため余力を生むことができます。発生した余力はさらに理想的な環境を整備するために使ったり、自分自身のスキル向上のために使えます。
    これを体の健康にたとえるならば、毎日暴飲暴食せず野菜を取り適度な運動を行ない十分な睡眠をとることで病気にならない様にするのが一番であって、自堕落な生活を送った結果重い病気にかかったので医者に行って治してもらうというのを繰り返すようでは体がどんどんぼろぼろになっていきます。体の健康に例えると、前者を行う人が尊敬され後者は笑いものにされるのですが、チーム運営となると前者は魔法か理想論と馬鹿にされ後者の出来る人が大人だと尊敬されているようで、どうも私はしっくりきません。何事も予防する、予防できる環境を作るのが一番です。発生してから耐えるのは美徳ではなくやむを得ないことです。
  • 課題: どうしても合わない人をどう追放するか
    これは世界全国、人とチームがある限りの永遠の課題だと思います。本当はこんなことをしないでよいのであれば理想なのですが、それでもどうしても人が余ったりチームに合わないからという理由で人を外さなければならないことがあります。アメリカ人みたいに非情にズバズバ首を斬ると、後から衝突が発生したりチームの財産を平気で奪って逃げたりされます。かといって日本人みたいに何時までも何時までも衝突を恐れて首を切れないと、チーム全体の死活に関わります。まったく、どうするのがいいんでしょう・・・><
■まとめ

3行でまとめると、楽しく生産性の高い仕事をするには、
責任感を持つ/持たせるのが大事
ゆとり大事
バランス大事
こんなところですかね。

2010年8月15日日曜日

CALayer を使って UIImage を描画する

UIImage を高速で描画する必要がある案件に遭遇したため、 CALayer を使ってみました。 CALayer と聞くとなにやら難しい感じがしますが、実際に使ってみると非常に簡単で高速です。

CALayer を使うと良い場面は以下のような場合です。
  • 画像を大量に描画する必要がある
  • 画像を高速に描画する必要がある
  • 画像を高速に変形・移動する必要がある
  • CGContextDrawImage を今使っている箇所がある
とくに変形に対して非常に強いです。 CGContextDrawImage で変形後の UIImage を再度描画し直したりするのに比べると、 CALayer の変形は格段に高速に動作します。


■実際に描画してみる

まず最初に <QuartsCore/QuartsCore.h> をインポートします。 QuartzCore.framework をプロジェクトに追加するのも忘れないようにしましょう。

準備ができたので描画します。 CALayer.contents プロパティに CGImageRef を渡すと後は全部勝手にやってくれます。簡単でしょ?
- (void)viewDidLoad {
    // UIImage* 型のプロパティ self.image があると仮定して・・・
    CALayer *l = [CALayer layer];
    l.contents = self.image.CGImage;
    l.position = CGPointMake(255, 255) // l.position はデフォルトではレイヤー中央の座標になります
    [self.view.layer addSubLayer:l];
}
CGContextDrawRect のような Core Graphics (Quartz) の関数を使用すると、座標系が左下基準になるため、 UIImage を描画する際にコンテキストの上下を反転してやらないと画像が上下反対に表示されてしまう問題がありますが、 CALayer はこの座標系の差異も勝手に考慮に入れてくれるので、 UIImage の CGImage プロパティをそのまま渡すだけでよく、ラクチンです。


■アニメーションしてみる

もともと CALayer は Core Animation フレームワークのクラスですから、当然アニメーションにも対応しています。といいますか、何もしないでそのまま CALayer のプロパティを変えると勝手にアニメーションします。 http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/AnimatingLayers.html の Animation > Implicit Animation あたりに記載されている内容がそれです。
// 既にこの l が addSubLayer されている場合、
// これだけで勝手にアニメーションします
l.transform = CGAffineTransformCreateScale(1.1, 1.1);
が、ときどきこのアニメーションが邪魔になる場合があります。そんなときは
// CATransaction というクラスを使用して, 一時的にレイヤーのアニメーションを切ります
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
                 forKey:kCATransactionDisableActions];
[aLayer removeFromSuperlayer];
[CATransaction commit];
// CATransaction というクラスを使用して, 一時的にレイヤーのアニメーションの時間を変化させます
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:10.0f]
                 forKey:kCATransactionAnimationDuration];
theLayer.zPosition=200.0;
theLayer.opacity=0.0;
[CATransaction commit];
こんな具合でアニメーションを調整できます。


■まとめ

たったのこれだけで画面上に高速でアニメーションもできる画像を描画することができます。お絵かきソフトなどで、画面上にユーザーが任意の画像を挿入できるようにしたい、と言ったときに大変役立ちますのでおすすめです。是非試してみてください。

2010年7月10日土曜日

virtualenv が上手く動作しない場合は -p オプションと --distribute オプションを試す

Python Hackathon という変態の巣窟に来ています。 virtualenv と buildout のハンズオンを受けているのですが、いくつか詰まった点があったのでメモ。

■virtualenv
普通にインストールするとき(ベースとなるpythonのsite-packageを受け継がないようにする場合)は以下のようにします。
python virtualenv.py --no-site-package myenv
ですがこの方法ではMac OS X 10.6付属のPython 2.6.1ではエラーになってしまいました。
New python executable in foo/bin/python
ERROR: The executable foo/bin/python is not functioning
ERROR: It thinks sys.prefix is '/System/Library/Frameworks/Python.framework/Versions/2.6' (should be '/private/tmp/virtualenv-1.4.3/foo')
ERROR: virtualenv is not compatible with this system or executable
そこで https://bitbucket.org/ianb/virtualenv/issue/17/virtualenv-not-working-with-default-python-on-mac-os を参考にして回避策を探してみたところ、
python virtualenv.py --no-site-package -p /usr/bin/python myenv
のようにして-pオプションを使いベースとなるPythonのパスを明示的に指定することで上手い具合にインストールできました。


■buildout
buildoutのbootstrap.pyをダウンロードしてきて実行すればよいのですが、Mac OS X 10.6付属のPython 2.6.1のsetuptoolsはバージョンが低い(0.6c9)ため、以下のようなエラーが出てしまうことがあります。
akisute server$ python bootstrap.py 
The required version of setuptools (>=0.6c11) is not available, and
can't be installed while this script is running. Please install
a more recent version first, using 'easy_install -U setuptools'.

(Currently using setuptools 0.6c9 (/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python))
もちろんsetuptoolsのバージョンを上げてもいいのですが、あまりデフォルトの環境を汚したくありません。そこで--distributeオプションを使用します。
python bootstrap.py --distribute
こうすると、distributeというsetuptools上位互換のモジュールが自動的にbuildout環境下にインストールされ、その結果上手い具合にインストールに成功します。buildout環境下へのインストールなのでデフォルトの環境は汚れません。すばらしい。

2010年7月7日水曜日

Core Graphics (Quartz) のみで日本語文字列を描画するライブラリのヘッダファイルを書いてみた

とある理由で UIKit の描画機能が使えず、 Core Graphics のみで文字列の描画処理を行わなければならないことになってしまったので、適当に調べてみました。


■ことのはじめ

Core Graphics の機能だけで日本語の文字列を描画する方法については、既に先人の方々が調べて記事にまとめてくださっていたので、そちらを見ていただければ大丈夫です。
http://iphone-dev.g.hatena.ne.jp/ktakayama/20100129
http://d.hatena.ne.jp/r_kurain/20100316
基本的にはこちらで紹介されている方法に従って進めていけば困ることはありません。ただし、描画した文字が上下反対になることがありますので、 CGContextSetTextMatrix を使う箇所を調整したりする必要があるかも。

で、最大の問題になるのがこれらの記事で紹介されている CGFontGetGlyphsForUnichars と呼ばれる関数です。この関数を使えば楽に日本語文字列を文字化けすることなく描画することができるのですが、あろうことかこの関数はプライベートAPIであり、使うとリジェクトされてしまうらしいです。ということで別の作戦をとらなければなりません。

上記の記事にいろいろな対処方法が載っているのですが、私は一番単純に
http://www.mexircus.com/codes/GlyphDrawing.mm
というライブラリを自分のアプリに組み込む方法を試してみることにしました。この方法を使えば、 CGFontGetGlyphsForUnichars でまず作って動くようにしてから、 CMFontGetGlyphsForUnichars と書き換えるだけでそのまま動作するので楽です。

問題はこのライブラリ、mmファイルしか用意されていないのでそのままではうまく使えません。適当にヘッダファイルを用意してやる必要があります。


■と言うわけで書いたもの

と言うわけで GlyphDrawing.h を書いてみました。
http://gist.github.com/466297

ライセンスは元の GlyphDrawing.mm に準じますが、 GlyphDrawing.mm のライセンスが不明なので、不安ならばまず作者さん (http://mexircus.com/blog/) に一言聞いてみるのがいいと思います。


■使い方
  1. http://gist.github.com/466297http://www.mexircus.com/codes/GlyphDrawing.mm をダウンロードします。
  2. GlyphDrawing.mm のインポートを以下のように書き換えます:
    //#import <Foundation/Foundation.h>
    #import "GlyphDrawing.h"
  3. GlyphDrawing.hGlyphDrawing.mm をプロジェクトに追加してビルドします。
  4. CGFontGetGlyphsForUnichars の代わりに CMFontGetGlyphsForUnichars を呼びだせばOKです。


■実際に使ったコードの例
NSString *message = @"本日は快晴なり";

// フォントを設定
// ついでにmessageのサイズも取得
// ただしsizeWithFontは UIGraphics の機能なので、本当に Core Graphics だけで描画したいならここで使ってはいけません
UIFont *font = [UIFont fontWithName:@"HiraKakuProN-W6" size:32.0];
CGRect messageRect = [message sizeWithFont:font];
CGFontRef fontRef = CGFontCreateWithFontName((CFStringRef)font.fontName);
CGContextSetFont(c, fontRef);
CGContextSetFontSize(c, font.pointSize);

// Glyphを作成
size_t length = [message length];
CGGlyph glyphs[length];
UniChar chars[length];
[message getCharacters:chars range:NSMakeRange(0, length)];
CMFontGetGlyphsForUnichars(fontRef, chars, glyphs, length);

// 文字列が上下反対になるのを防止する
CGAffineTransform transform = CGAffineTransformMakeScale(1.0, -1.0);
CGContextSetTextMatrix(c, transform);
CGContextTranslateCTM(c, 0, messageRect.size.height/2);

// 描画
CGFloat x = 100.0f;
CGFloat y = 100.0f;
CGContextShowGlyphsAtPoint(c, x, y, glyphs, length);

2010年6月26日土曜日

別スレッドで NSURLConnection を使うときのメモ

http://twitter.com/griffin_stewie/status/17022582070

@griffin_stewieさんにご指摘いただいたので調べてみました。

別スレッドでNSURLConnectionを使おうとすると、そのままではNSRunLoopのモードの問題なのか、上手い具合にデータを受信することが出来ません。私はメインスレッドでNSURLConnectionを動かすようにして難を逃れたのですが、それ以外にも教えていただいた方法を用いて、以下のように別スレッドのNSRunLoopのモードを変更して対応すると良いみたいです。
    NSURLRequest *theRequest = [NSURLRequest requestWithURL:iTunesURL];
// create the connection with the request and start loading the data
rssConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
// This creates a context for "push" parsing in which chunks of data that are not "well balanced" can be passed
// to the context for streaming parsing. The handler structure defined above will be used for all the parsing.
// The second argument, self, will be passed as user data to each of the SAX handlers. The last three arguments
// are left blank to avoid creating a tree in memory.
context = xmlCreatePushParserCtxt(&simpleSAXHandlerStruct, self, NULL, 0, NULL);
if (rssConnection != nil) {
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (!done);
}
runMode:beforeDateを使うわけですね。なるほど。