2012年12月3日月曜日

Unity Asset Server でブランチ切ったりタグつけたりする方法

弊社のUnityプロジェクトではUnity Asset Serverを使ってソースコードやアセットの管理を行なっているのですが、以前ブログに書いた通りこのUnity Asset Serverさんは標準でブランチもなければタグもサポートしていないというCVS以下の素敵なシロモノです。しかしながらやはり開発のニーズ上、継続的に開発するためにはブランチやタグが必要になってきます。そこでUnity Asset Serverを使っていかにしてブランチやタグを運用するかという方法を考えてやってみましたので共有いたします。


■基本方針

基本方針は要するにSVNのやり方をパクります。すなわち、
  • タグやブランチはリポジトリのコピーとして表現する
  • リベースやマージは気合でなんとかする
以上二点です。

さてリポジトリのコピーですが、一応Unity Asset Serverではリポジトリのコピーの作成が可能になっています。



こちらをクリックしてリポジトリをコピーしてください。注意点としてはこのリポジトリコピーは他のユーザーが一人でもAsset Serverに接続しているとコピーが出来ないので、社内に呼びかけて全員のUnityをシャットダウンするなり何なりしてください。誰が接続しているか特定したい場合はUnity Asset Serverが動いているサーバで
ps aux | grep unity
とか叩けばなんとなくわかります。


■タグを付けたい

これは簡単です。Asset Serverに接続して、タグが付けたいバージョンがヒストリの先頭にある状態で、先述したリポジトリのコピーを行います。あとはコピーしたリポジトリに触らないようにすればタグの完成です。

注意点として、タグが付けたいバージョンがヒストリの奥のほうにあると基本的には綺麗にタグが切れません。そのようなコミットをする前にタグを切ってください。


■ブランチを切りたい

これもまだ簡単です。Asset Serverに接続して、ブランチの根っこにしたいバージョンがヒストリの先頭にある状態で、先述したリポジトリのコピーを行います。あとはこちらのリポジトリに作業者全員が接続先を切り替えて、新しいコミットを重ねていけば良いです。

注意点として、タグと同様に例によってバージョンがヒストリの奥のほうにあると綺麗にブランチが切れず苦労することになりますので、コミットの重ね方には最新の注意を払う必要があります。

それから各作業者がブランチを切り替える際の手順にも注意する必要があります。
  1. Asset Serverに接続してdisplayを選択。
  2. 新しいブランチとなるリポジトリを選択してconnect。
  3. リポジトリの切り替えが終わったらupdateを行う。
  4. マージするかどうか聞かれたら、ブランチの切り替えなので、すべてDiscard my changesを選択すること。

■ブランチをリベースしたい

ここからがちょっとした地獄の始まりです。まずはリベースから。例として1.0.1ブランチのブランチ元である1.0.0をリベースする手順を示します。
  1. Asset Serverに接続して、リベース元となるブランチ (1.0.0) にconnect。
  2. リポジトリの切り替えが終わったらupdateを行う。
  3. マージするかどうか聞かれたら、ブランチの切り替えなので、すべてDiscard my changesを選択すること。
  4. Asset Serverに接続して、リベース先となるブランチ (1.0.1) にconnect。
  5. リポジトリの切り替えが終わったらupdateを行う。
  6. マージするかどうか聞かれたら、リベース元のコードをすべて新しい接続先のブランチに上書きしたいという処理になるので、ソースコードとシェーダーコードについてはMergeを、その他のシーン、プレファブ、テクスチャ、モデル、音などはすべてIgnore server changesを選択する。
  7. このままだとなぜかコードは変更されているのに変更差分をUnityが認識しない悲しい状態になるので、メニューからAssets > Reimport allを選択して変更差分をUnityに認識させる。
  8. マージに失敗したコードおよびシーン、プレファブ、テクスチャ、モデル、音などを手でマージする。
  9. コミットして完了。
これで何とかリベースっぽいことができるようになります。


■ブランチをマージしたい

ブランチのマージは単にリベースの逆をやるだけです。例として1.0.1ブランチをmasterにマージする手順を示します。
  1. Asset Serverに接続して、マージ元となるブランチ (1.0.1) にconnect。
  2. リポジトリの切り替えが終わったらupdateを行う。
  3. マージするかどうか聞かれたら、ブランチの切り替えなので、すべてDiscard my changesを選択すること。
  4. Asset Serverに接続して、マージ先となるブランチ (master) にconnect。
  5. リポジトリの切り替えが終わったらupdateを行う。
  6. マージするかどうか聞かれたら、マージ元のコードをすべて新しい接続先のブランチに上書きしたいという処理になるので、ソースコードとシェーダーコードについてはMergeを、その他のシーン、プレファブ、テクスチャやモデルや音などはすべてIgnore server changesを選択する。
  7. このままだとなぜかコードは変更されているのに変更差分をUnityが認識しない悲しい状態になるので、メニューからAssets > Reimport allを選択して変更差分をUnityに認識させる。
  8. マージに失敗したコードおよびシーン、プレファブ、テクスチャ、モデル、音などを手でマージする。
  9. コミットして完了。
見ての通り、リベースとほとんど同じですね。

2012年12月1日土曜日

PySpa アドベントカレンダー 1日目 〜バカとPythonと芳泉閣〜

PySpaアドベントカレンダーというイベントに参加してみたところ、僭越ながら初日を努めさせていただくことになりました>< ということで本日は技術ネタではなくてPySpaというイベントとともに自分の職歴でも振り返ってみようと思います。


■そもそもPySpaってなんぞや

vの人という主犯格が定期開催している温泉旅館に泊まるイベントです。名前に反して実はPythonは全然関係ないです。詳細は以下のページとか見てみてください。
https://github.com/pyspa/pyspa
http://voluntas.hatenablog.com/entry/20081024/1218125470

基本的にvの人の知り合いばかりで固定PySpaメンバーと言えるメンバーがいるのですが、昔からいた人が仕事の都合とかで疎遠になると新しい人が定期的にやってきて全体が保たれているという感じの不思議な輪が形成されているのが特徴ではないかと思っています。後、全員やたら技術力があります。


■SIer時代(2008~2009年)

最初に参加したのが2008年に開催された第4回です。記憶が曖昧なのですが、確かTwitterだか何だかで参加者を探していたところに無理やり飛び込んだのがきっかけです。よくやった当時の自分。当時は新卒入社したSIerの会社でJavaを書いていて、Pythonって面白そうだなーとか考えて手を出していたころだったのでPythonの勉強会のノリで参加してました。あとそういえばiPhone 3Gを発売日当日に買い、Apple信者生活を開始しました。

第4回の記録
http://akisute.com/2008/10/4python-1.html

うん、今見たら恥ずかしくて死にたい。といっても年中後から振り返ったら恥ずかしくて死にたいようなことしかしてない気がするので今更気にしたら負けです。次行きます。次。
第5回からは完全にiPhone開発ばっかです。あとこの一年で随分PySpaのノリを理解した感じを文体から感じ取れます。

第5回の記録
http://akisute.com/2009/06/5-python.html

この期間は自分の基礎となる技術や興味を見つけることができた貴重な時期でした。最も向上心がありバックグラウンドもない新人時代を非常に有意義に過ごすことができたのではないかとおもいます。あと、今現在わずかに残っているのみとなった社会人として必要な常識などは全てこの時期に身につけさせていただきました>< 本当にありがとうございました><


■Web屋時代(2010年~2011年)

2009年末に新卒入社したSIerの会社を退職してビープラウドに転職しました。転職理由はもはや覚えていないのですが、確かこのままこの会社にいてはスキルアップにならない!よりよい環境へ移るべきだ!などという意識の高い社二病精神か何かが原因だった気がします。

このへんから面倒になったせいかPySpaメンバーの内輪みたいな感じになったせいもあるのかブログへの参加記録が途絶えてしまっていますが、PySpa自体には二年間毎回かかすことなく参加しておりました。というのもPySpa自体がビープラウドの非公式社外リクルートイベントみたいな感じになっていた関係上、半数近くの社員がPySpaに参加する時があったりしたからです。

例えば今回のアドベントカレンダー参加メンバーだと

12/1 akisutesama
12/3 tokibito
12/4 torufurukawa
12/8 shimizukawa

あたりがPySpa経由で雇用されたビープラウド社員だったりします。

第6, 7, 8, 9, 10と参加してきたPySpaですが、10回でいったん定期開催を締めようというvの人の考えで第10回を最後にしばらく開催が途絶えてしまいました。そのかわり定期開催する文化はPyFesへと引き継がれていきました。

この期間は良い意味でも悪い意味でも自分のアイデンティティが確立された期間でした。何か捨ててはいけないたぐいの常識を投げ捨ててしまった変わりに職業iOSプログラマとしてのキャリアが確立された感じでしょうか。あとは仕事でひどく失敗した時期でもありました。前職SIer時代では失敗ができるような機会はなかったのですが、ビープラウドでは数回ひどく失敗してご迷惑をお掛けしたことがありました。ほんとスミマセン>< お陰様で人類としての社会常識はなくなりましたがプロとして仕事を進める常識は多少身につきました><


■ゲーム屋時代(2012年~)

2012年の2月にビープラウドを退職して変なゲーム会社に転職して以来、しばらくご無沙汰していたPySpaですが、今年の10/26に開催されたのでふらっと参加してきました。

感想、みんな年食い過ぎ。

高齢化の並はPySpaにも押し寄せているのかいないのかわからんのですが、第4回第5回の頃の大学生のきゃっきゃうふふ的ノリはかなり鳴りを潜め、仕事の話をしたりだとかが増えたり徹夜組が全滅して朝早起きになってたりお菓子の減りが悪くなってたりと随所におっさん臭がしてきております。ピンチです>< まぁ時間の流れというのはそういうものなのかなぁと私自身おっさん的に思っております。

この期間は自分のこれまでの集大成であるような気がしています。子供の頃にハマりまくっていたゲームへの愛情と、SIer/Web屋時代に培ったサーバ側技術と、iPhoneリリースからやってきたiOS開発の集大成という感じです。そして随分と責任ある立場をまかされるようになってしまった気がします。うーん年だ。このあとどうしよう。とりあえず今しばらくはゲームが最高に面白いのでゲームをやろうと思ってます。

余談ですがうちらのゲームもうすぐリリースらしいです。一応宣伝。
http://www.donpy.net/appinfo/18791.html




■まとめ

こうして振り返ると私の仕事人生はiPhoneとPySpaとともに歩んできたんだなぁと思います。iPhoneは私に夢中になって開発できる何かをくれましたし、PySpaは私にスキルと人脈と情熱をくれました。この2つがあったからこそここまでやってこれたんだなぁと思っています。これからもまぁそんな感じで適当にやっていきます。

最後になりましたけれども、PySpaを毎年開催してくださっているvの人と、今回のアドベントカレンダーを企画していただいたとんぷーさん、どうもありがとうございます!

それでは明日はshkumagaiさんよろしくおねがいしまーす!

2012年11月12日月曜日

Unity 3.5 以下でプリプロセッサ定義を上手く使う方法

C/C++/Objective-Cベースのプロダクトのビルドシステムを構築するときに大変便利なのがプリプロセッサのdefineマクロです。defineマクロを使って環境を切り替えたり不要なログコードを削除したりなどは皆さんよくやってるかと思います。C#にも同様のプリプロセッサ定義が用意されているみたいですので、こちらを使ってUnityプロジェクトのビルドシステムを構築できないかと思ってやってみました。


■プリプロセッサ定義の使い方 (Unity 4以降)

Unity 4.0よりプリプロセッサ定義がUnity本体によってサポートされるようになりました!ビルドスクリプトからも自由にアクセスが可能になっており、非常に簡単に扱うことが可能です。
詳しくはけいごさんのブログに完璧にまとめられていますのでそちらをご覧ください。
http://anchan828.tumblr.com/post/32669868103/define


■プリプロセッサ定義の使い方 (Unity 3.5以降)

ここからが本題になります。Unity 3.5以前はプリプロセッサ定義を公式にサポートしていません。そこで以下の4種類のファイルをAssetsフォルダ直下に置くことでプリプロセッサ定義を読み込ませます。
/Assets/smcs.rsp (C#用)
/Assets/gmcs.rsp (C#エディタスクリプト用)
/Assets/us.rsp   (UnityScript、俗にJSと呼ばれている物用)
/Assets/boo.rsp  (Boo用)
このrspファイルにはC#コンパイラのコンパイラオプションとして渡すオプションが記述できます。ですので http://linux.die.net/man/1/mcs などに列挙されているコンパイラオプションであればひと通りなんでも使用可能です。今回はプリプロセッサ定義をやりたいので、以下のように書けばOKです。
-define:DEBUG
複数の定義が書きたい場合は、
-define:DEBUG -define:USE_DEV_SERVER
のようにスペースで空けて複数記載するか、
-define:DEBUG
-define:USE_DEV_SERVER
のように改行して複数記載するか、
-define:DEBUG;USE_DEV_SERVER
のようにセミコロンで列挙して書くことができます。注意点として、C/C++/Objective-Cのdefineマクロと違って
-define:DEBUG=1
のように値を定義することはできないみたいです。あくまでシンボルを作るだけみたいですね。

以下Twitterでのやりとりです。

さて、これでプリプロセッサ定義はバッチリできるようになったのですが、問題はこれをビルドごとに書き換える方法です。最初はビルドスクリプトでこのrspファイルを書き換えていたのですが、どうもこれをUnityが全く認識しないのです。普通にファイルをただ書き換えてもダメなようです。

そこで黒魔術を導入します。以下のスクリプトで使われているコードを利用して、無理やり書き換えた後のrspファイルをUnityに認識させます。
https://github.com/prime31/P31UnityAddOns/blob/master/Editor/GlobalDefinesWizard.cs#L131
AssetDatabase.Refresh();
reimportSomethingToForceRecompile();

private void reimportSomethingToForceRecompile()
{
 var dataPathDir = new DirectoryInfo( Application.dataPath );
 var dataPathUri = new System.Uri( Application.dataPath );
 foreach( var file in dataPathDir.GetFiles( "GlobalDefinesWizard.cs", SearchOption.AllDirectories ) )
 {
  var relativeUri = dataPathUri.MakeRelativeUri( new System.Uri( file.FullName ) );
  var relativePath = System.Uri.UnescapeDataString( relativeUri.ToString() );
  AssetDatabase.ImportAsset( relativePath, ImportAssetOptions.ForceUpdate );
 }
}
これでC#で書かれたコードに対しては無事にrspファイルへの変更が反映されるようになります。

しかしながら更に落とし穴があります。UnityScript(JavaScript)で書かれたファイルに対してはこの黒魔術が効きません。したがってUnityScriptで書かれたファイルに対して動的にus.rspファイルの定義を適用することができないようです。すべてのJSファイルに対して上記の黒魔術を適用しても効果がなかったため、おそらくダメだと思われます。


■結論

UnityScriptは使わないようにしましょう。

2012年11月11日日曜日

iOS 6.0の advertisingIdentifier と identifierForVendor にはバグがあるので注意

いささかタイミングを逃した感が強いのですが、厄介なバグにぶち当たってしまったので共有いたします。

iOS 6からUDIDに変わる識別子としてUIDeviceのidentifierForVendorとASIdentifierManagerのadvertisingIdentifierが使えるようになったのはすでにみなさんご存知かと思います。ですがどうもこやつらiOS 6.0だと正しく機能しない場合があるようなのです。

詳細は以下のとおり。
http://stackoverflow.com/questions/12605257/the-advertisingidentifier-and-identifierforvendor-return-00000000-0000-0000-000

こちらの情報元によると、iOS 6.0に Over-The-Air アップデート (iTunesを使わないで端末からアップデートする方法) するとこれらの識別子が常に00000000-0000-0000-0000-000000000000を返してしまうらしいのです!iOS 6.0.1では修正されているらしいです。

見事に私の UIApplication-UIID ライブラリもこのバグを踏んづけて大爆発してしまいました。

対処法としては生成されたIDが00000000-0000-0000-0000-000000000000でないか文字列比較する方法がよさそうです。

2012年9月26日水曜日

Unity の PostprocessBuildPlayer を使って Weak Framework を追加する方法

※2013/11/27追記: 第二版を公開しました。

UnityでiOSのアプリを作っていて困ることの一つに、iOSが提供するシステムフレームワークへのリンクをプロジェクトに追加するのが超面倒くさいという問題が挙げられます。UnityがiOSアプリを書きだした後、手動でXcode上からシステムフレームワークを追加してもいいのですが、これはとんでもなく面倒です。

そこでUnityジャパンの伊藤さんという方が PostprocessBuildPlayer という仕組みと Ruby の xcodeproj ライブラリを使ってビルド時に自動的にシステムフレームワークを追加する仕組みを公表してくださいました。お陰様で随分とはかどっていたのですが、その方法では実はシステムフレームワークをWeakリンク(Optionalリンク)することができなかったのです。これでは例えばSocial.frameworkを使うアプリをビルドしてiOS5で動かすと起動時に問答無用でクラッシュしてしまいます。困りました。iOS6/5両対応ができないと話になりません。

というわけで作りました。システムフレームワークをWeakリンクできるPostprocessBuildPlayerを。こちらです。


ライセンスはMITライセンスにしておきました。
使い方は大体見ればわかるかと思いますが、まず最初にgem経由でxcodeprojをインストールしておくこと。
sudo gem install xcodeproj
あとは上記のPostprocessBuildPlayerをUnityプロジェクトの /Assets/Editor 以下に配置して、コード中のフレームワーク名を指定している箇所をご自身のお好きなように変更してやればオッケーです。

Unity 3.5.5 以下で Game Center / iAd / UIImagePicker などを使用したアプリが iOS 6 でクラッシュする問題

現在 Unity 3.5.5 以下でビルドしたiOSアプリが、以下の条件をすべて満たしているとクラッシュしてしまう問題が発生しているようです。
  • UnityのiOSアプリビルド設定で、画面方向を横向き(Landscape Left/Landscape Right)のいずれかのみに設定している。
  • Pluginなどを経由してGame Centerを使用している。またはiAdやUIImagePickerなどのiOSが提供する特定のView Controllerを使用している。
  • iOS 6を搭載したiPhone/iPod Touch上で動作させる(iPadは大丈夫)。
詳細はこちら。
http://developer.coronalabs.com/forum/2012/09/17/ios-6-orientation-crash





すでにUnity側で問題は把握しているようで、現在修正版の3.5.6を用意してくださっているようなので、続報を待ちましょう。・・・といっても、すでにUnity 3.5.5以下でビルドされたiOSアプリをリリースしていて、しかもAsset Bundleを使用していたりすると、Asset Bundle間の後方互換性問題のためかなり厄介なことになると思われます。最悪過去のバージョンのサポートをすべて切る必要が出てくるかもしれません。


さて、ここからは技術話の余談です。

このクラッシュ問題なのですが、原因はiOS 6で変更になった画面回転(Auto Rotation)APIにあると思われます。iOS 6からはなんとこれまで画面回転に使用されていた
UIViewController shouldAutorotateToInterfaceOrientation:
が完全に廃止になっており、基本的には全く呼び出されないようになってしまっています。その代わりにより体型だった画面回転の仕組みが導入されています。iOS 6からの画面回転は、「アプリが対応する画面方向」と「各View Controllerが対応する画面方向」の2つによって画面の回転する方向が決定されるという仕組みになっています。

ここで、アプリが対応する画面方向は以下のように決定されます。
以下優先順位順に、
1. UIApplicationDelegate application:supportedInterfaceOrientationsForWindow: が返す向き。iOS 6以降のみ。
2. UIApplication supportedInterfaceOrientationsForWindow: が返す向き。iOS 6以降のみ。
3. Info.plist で指定されている UIInterfaceOrientation の向き。
各View Controllerが対応する画面方向は以下のように決定されます。
各ViewControllerが supportedInterfaceOrientations を実装しているなら、それが返す向き。
していないならば、
iPhoneだと UIInterfaceOrientationMaskAllButUpsideDown
iPadだと   UIInterfaceOrientationMaskAll
この2つの積集合をとって、両方が一致した向きに画面回転が行われる仕組みになっています。

では、ここで両者が全く一致しない場合はどうなるでしょう?答えは簡単でクラッシュします。ワオ。素敵。ふざけんなバカApple爆発しろ。

それを踏まえると、今回の問題でクラッシュしてしまう仕組みはこう考えられます。
  1. Unity 3.5.5以下が書き出すiOSアプリは当然ながらiOS 6に完全対応していない。
  2. Game Centerなど、Appleが提供しているUIコンポーネントは全てiOS 6の画面回転に対応しているが、それらのうち幾つかのものはiPhoneだと縦向き画面にしか対応していないものがある(iPadは基本縦横どちらでも表示できるように対応する必要があるとされているため、無事のようです)。
  3. Unityが書きだしたアプリは横向き画面にしか対応していないのに、上記のView Controllerは縦向きにしか対応していないと言い出すので、クラッシュする。
やれやれですね><
ちなみに対応策としては、アプリ自体は縦方向にも横方向にも対応しているというふうにapplication:supportedInterfaceOrientationsForWindow:メソッドを使って値を返すようにした上で、コンテンツを表示するUIViewをUIViewControllerに管理させるようにして、UIViewControllerのsupportedInterfaceOrientationsで横向き画面の値だけを返すようにするといいかんじになると思います。しかしながらiOS 5/6両方で上手く回る画面は結構大変そうです。

2012年8月19日日曜日

Unity のプラグインで iOS の bundle を使えるようにする方法

OS XやiOSには Bundle という仕組みがありまして、 NSBundle というBundleを扱うためのクラスが用意されています。皆様も一度は
[[NSBundle mainBundle] pathForResource:@"MyMusic" forType:@"mp3"];
みたいなコードを書いたことがあると思います。このBundleの仕組みを使うと、
  • 複数の画像や文言、JSONなどのデータファイル、音楽などを一つにまとめて扱うことができる。個別のファイルとしてプロジェクトに加えなくてよいので利便性が良い。
  • Bundleには最初から多言語化リソースを扱うための仕組みが用意されているため、多言語化が非常に簡単にできる。
  • OS Xだけになりますが、Bundleにはコンパイル済みのコードを含められるので、プラグインの仕組みが簡単にできる。
以上のようなメリットがあります。例えば実例を上げると、Facebook SDKなどがBundleの中に画像や文言などのリソースを格納して配布するようになっています。実際には確認できていないのですが、おそらくmobage, GREEのSDKもそのようになっているのではないでしょうか?ということでSDKなどを配布するときには非常に便利です。

で。このBundleによる配布はなかなか便利なので、UnityのiOSプラグインとして配布するときにも是非使いたいのですが、そのまま/Assets/Plugins/iOS以下にBundleを配置しても正しくBundleが認識されませんし、iOSのアプリにインストールされません。

そこでBundleを配布するときは、/Assets/Plugins/iOS/以下ではなくて、/Assets/StreamingAssets/以下にBundleを配置するようにしましょう。実は/Assets/StreamingAssets/以下のパスには隠し要素があって、このパス以下のファイル・ディレクトリは全てアプリケーション自身の/Data/Raw/ディレクトリに配置されるようになっています。
参考: http://sehm.blog48.fc2.com/blog-entry-159.html

あとはプラグインとして用意したiOSコードの中で以下のようにしてBundleを取得すればOKです。
NSBundle *bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"MyBundle" ofType:@"bundle" inDirectory:@"Data/Raw"]];
これでUnityプラグインを多言語化したり画像リソースを使ったりするのが楽になると思います。

将来的にUnityのバージョンが上がって、/Assets/Plugins/iOS/以下においたBundleも扱えるようにしてくれると楽なんですが・・・Unity 3.5の地点ではどうもダメっぽいです。

2012年7月25日水曜日

LLDB を使って CUI で動作させているテストケースをデバッグする

GHUnit のテストケースを Jenkins で自動的に走らせるために CUI のテストを作っているのですが、 GUI を使ったテストでは問題なく通るのに CUI を使ったテストのときだけテストが落ちるという問題が発生してしまいました。 Xcode 経由の実行であれば Instruments だろうが内臓のデバッガだろうが使い放題なのですが、 CUI となるとそうもいきません。そこで LLDB を直接使って CUI で動作させているテストケースを直接テストしてみることにしました。

参考にしたのはこちら。
http://lldb.llvm.org/tutorial.html

さっそくやってみましょう。lldbコマンドで LLDB の対話環境を起動した後、
process attach --name MyApp --waitfor
でMyApp.appという名前のプロセスが立ち上がるのを監視します。



この状態でMyApp.appというアプリを起動すると、上の画像のようにばっちり LLDB がプロセスを検知して捕まえてくれます。今、MyApp.appはポーズ中になっているので、適当な箇所に
breakpoint -f MyTestModel.m -l 123
とかやって適当にブレークポイントを設置した後、
thread continue
で続きを実行開始します。



ご覧のとおりバッチリブレークポイントで捕まえることに成功です。こちらの図はEXC_BAD_ACCESSが発生した時になんか自動的に止めてくれたときのものです。Xcode経由で起動するときに比べれば不便ですが、何もないよりはだいぶはかどりますよ。

2012年6月24日日曜日

__has_feature(objc_arc_weak) を使って賢く安全に ARC/Blocks を使う

iOS 6も発表されて、皆さんARCやBlocksをガンガン使用する感じのプログラミングスタイルに変化してきていると思うのですが、そこで問題になってくるのが後方互換性の話です。特にiOS 4。Blocksを使うとなるとどうしても以下の様に非同期で実行されたBlocksの中からViewを書き換えるようなコードを書きたくなるのですが、
__weak MyViewController *__weakSelf = self;
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse* response, NSData* data, NSError* error){
    __weakSelf.label.text = [[NSString alloc] initWithData:data encoding:NSUTF8Encoding];
}];
このようなコードを安全に実行するためにはselfを一度__weakな変数に代入して、それをBlocksにキャプチャさせるようにしないと、以下のような理由で安全ではなくなってしまいます。
  • __strongを使うと、対象の変数をキャプチャしているBlocksの実行が終わるまで対象の変数がreleaseされなくなるばかりか、最悪の場合は循環参照が発生してメモリが絶対に開放されなくなってしまいます。
  • __unsafe_unretainedを使うと、Blocksの実行が終わるまでの間に対象の変数がreleaseされてしまうとEXC_BAD_ACCESSでクラッシュします。
しかしながらiOS 4では__weakが使えず、状況に応じて__strongや__unsafe_unretainedでごまかす必要があります。このようなときにiOS 5では__weakを使い、iOS 4やno-ARCなプロジェクトではそれなりに適切な何かを使って実装するような仕組みが欲しくなります。

そこで便利に使えるのが__has_feature(objc_arc_weak)__has_feature(objc_arc)マクロです。こいつらを使うと簡単に現在のビルド環境・ターゲット環境がARCを導入しているか、weakは使用可能か、を判断できます。たとえば私は以下の様なマクロを組んで、
// ARC & memory management
// Use these prefixes to be compatible with ARC on iOS 5/ ARC on iOS 4.X / non-ARC
// 
#if __has_feature(objc_arc_weak) // iOS 5 or above
#define __my_block_weak        __weak
#define __my_block_weak_unsafe __weak
#elif __has_feature(objc_arc)    // iOS 4.X
#define __my_block_weak        __strong
#define __my_block_weak_unsafe __unsafe_unretained
#else                            // iOS 3.X or non-ARC projects
#define __my_block_weak        __strong
#define __my_block_weak_unsafe __block
#endif
こんな感じのコードにしてます。
__my_block_weak MyViewController *__weakSelf = self;
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse* response, NSData* data, NSError* error){
    __weakSelf.label.text = [[NSString alloc] initWithData:data encoding:NSUTF8Encoding];
}];
こうするとどの環境でも(比較的)安全にblocks内部でselfを触ることができるという寸法です。

ここで気になるのが__has_feature(objc_arc_weak)の判定条件です。個人的にこいつはiOS SDK 5.0以上を使っていたらターゲット環境がiOS 4とかiOS 3とかでも無条件で1を返してしまって使えないんじゃないのかと危惧していたのですが、なんとIPHONEOS_DEPLOYMENT_TARGETの値を見てきちんと値が変化する仕組みになっています!なので例えばSDKは最新だけれどiOS 3もサポートしたいみたいなプロジェクトでは__has_feature(objc_arc)の値が自動的に0になって良い感じに分岐してくれるというわけです。安心して使いまくりましょう!

2012年5月5日土曜日

C# で map とか reduce みたいな楽しいリスト操作をしたい

プログラミング言語がロジックを組むのに向いているかいないかを判断するときの基準に、私はよく「文字列の操作が優れているか否か」「配列・辞書・集合の操作が優れているか否か」を評価点に挙げます。文字列や配列、辞書、集合の操作はほとんどすべてのアプリケーションで必要になり、それらの生産性が高く高速に処理してくれる言語ほど簡単で高速なロジックが組めると思うからです。

そういう意味でObjective-Cを考えると、文字列の操作はまぁまぁ良い(特にUnicode周りがなかなか優れている、正規化もできるし)のですが、配列・辞書・集合の操作がイマイチで、作るの面倒なら操作するのも面倒。さらには良く欲しくなる以下の操作が欠けています。
  • map - 条件式を渡して、もとの集合の各要素に条件式を通した結果を新たな集合として返す。
  • reduce - 条件式を渡して、要素を前から順番に計算して畳み込み、集合から一つの要素にする。
  • any - 一つでも要素が条件式を満たすならtrue, すべての要素が満たさないならfalse
  • all - すべての要素が条件式を満たすならtrue, 一つでも満たさないならfalse
ではMono上のC#ではどうなんだろうということで調べてみたところ、LINQを提供するSystem.Linq名前空間に高度な配列・辞書・集合操作を行うための拡張メソッドが用意されているということがわかりました。

http://docs.go-mono.com/?link=T%3aSystem.Linq.Queryable
http://msdn.microsoft.com/ja-jp/library/system.linq.queryable(v=vs.90).aspx

ということで早速使ってみます。



実行速度が高速なのかどうかはわからないのですが、なかなか面白いです。ラムダ式が使えるのもスマートで素敵ですね。うーん、C#好きになってきたかも。

Mono の System.String と string の違いについて調べてみた

MonoとかC#とか初心者なのでSystem.Stringとstringの違いがよくわからず、気になったので調べてみました。

以下のページのベストアンサーが一番わかり易かったです。
http://okwave.jp/qa/q7225655.html

ちょっと転載します。
string と System.String は型として使われる限り単なる別名なので同じものです。
foo.System.String みたいなものが用意されて foo 名前空間内から使うような場合は異なりますが,特殊例過ぎるのでこれは考えないものとします。
コンパイラは,ソース上の型として string と書かれているものは,System.String とかかれているものとして処理します。
# 正確には "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" という型 (.NET 4 の場合)

これは,SDK 付属の逆アセンブラでコードを見ると,string を使っても System.String を使っても同じコードになることから確認できます。

同じように,int は System.Int32 の別名ですし,bool は System.Boolean の別名です。


使い分けは,特別決まりはありません。
唯一,メソッド名等に使う場合には言語固有の名称を避ける (CLR の名前を使う),というのはあります。
MSDN: 一般的な名前付け規則
http://msdn.microsoft.com/ja-jp/library/ms229045.aspx
具体的には,
・ToInt
よりも,
・ToInt32
の方が好ましい,ということです。
# 上側の名前付け規則だと,Visual Basic においては ToInteger であるべき,となる。


私は,static メソッドの呼び出しに使う場合は CLR の名前を使い,それ以外では言語固有の名称を使っています。
書籍によっては,常に CLR の名前を使うべき,という物もあったりします。
# が, for (Int32 i = 0; ……みたいなコードはほとんど見ません。

自分なりの物でいいので,統一したルールを用意しておくのがよいでしょう。
# 常に言語固有の名称でもよいと思います。

そもそも.NET Framework (とMono) は実装言語に依存しないような作りになっている (CLRというやつ?) のでこのような仕組みになっているようです。なるほど。 int, bool, string はC#の型で、 System.Int32, System.Boolean, System.String はCLRの型なわけですな。勉強になりました。

Unity でシステムが使用しているリソースを一覧表示したい

簡単なEditor拡張機能を書くときとか、デフォルトでUnityのエディタが使っている画像とか借りて表示したいときが多々あるんですが、これが実は公開されていなくてそのままでは使えないのです。ということでデフォルトでUnityのエディタが使っているリソース一覧を暴くスクリプトを用意してみました。



実行するとログに全てのリソースの一覧が表示されますので、あとはそれっぽい名前のやつを適当に拝借すればOKです。

自作の Unity Editor Script を github に公開してみた



最近は仕事の関係でUnityばっかり触っているのですが、中でも今お気に入りなのがEditor拡張機能です。これは自分で簡単なスクリプトを書くだけでUnityのエディタ上の表示を自由にカスタマイズできる機能なのですが、コイツの出来がなかなか素晴らしいのです。これまでいろんな開発環境を触ってきたのですが、ここまで簡単にエディタ上の表示を自在にカスタマイズできる環境は他に類がありません。

ということで調子に乗って自作のEditor拡張機能を書いて公開してみることにしました。まずは簡単なものからということで、シーン上に配置できる定規を作りました。


■機能
  • 原点からの距離(u)、高低差(u)、対象位置地面の水平方向傾き(度)を測定可能 ※1u=Unity上での長さの単位1とします
  • イメージを見ての通り全数値がエディタ上に表示されるので一目でわかる
  • Raycast飛ばしてるので確実に地面位置を基準にして判定してくれる、Raycastが外れても空中の座標がきちんと出る隙を生じぬ二段構え
  • 画面上の色/フォントを自由にインスペクタから変更可能
  • すべてEditorScript/Gizmoなので万が一何かの間違いでリリース版のシーンに残っていてもMesh Rendererが走って画面に表示されるという悲しい思いをしない
  • 操作は原点を選択してAdd Pointボタンを押して出てきた点を任意の位置に移動して配置するだけ
  • Clear Pointボタン付き
ひと通り基本的なのは揃えてみました。


次は高低差メッシュをTerrainの上に表示する機能とか作ってみたいっすね。

2012年5月4日金曜日

Unity の GUIStyle でデフォルトで指定できるスタイル一覧

今回は完全に自分用メモになってしまいますがご了承ください><

UnityのGUIStyleはname文字列を指定して生成する事が可能ですが、Unityがデフォルトで持っているGUIStyleの一覧を見つけたので列挙しておきます。
  • box
  • button
  • toggle
  • label
  • textField
  • textArea
  • window
  • horizontalSlider
  • horizontalSliderThumb
  • verticalSlider
  • verticalSliderThumb
  • horizontalScrollbar
  • horizontalScrollbarThumb
  • horizontalScrollbarLeftButton
  • horizontalScrollbarRightButton
  • verticalScrollbar
  • verticalScrollbarThumb
  • verticalScrollbarUpButton
  • verticalScrollbarDownButton
  • scrollView
大文字小文字は関係ないみたいです。たとえばboxのところをBoxと指定しても問題なくそのまま通ります。

参考URL: http://answers.unity3d.com/questions/9844/copypaste-guistyle-in-the-inspector.html
参考URL: http://unity3d.com/support/documentation/Components/gui-Customization

2012年4月22日日曜日

Unity Asset Serverを導入してみた

先日のiphone_dev_jp東京の勉強会での発表資料に書いた内容と同じなのですが、結局あの場では発表できませんでしたので、改めてUnity Asset Serverを触ってみた感想などを書いてみようかと思います。


■Unity Asset Serverって何?

Unity社が提供している、Unityのエディタと完全に統合されたバージョン管理システムです。
http://unity3d.com/unity/team/assetserver/
ソースコードだけではなく、プレファブやシーンを含めたUnityのプロジェクト全体のリソースを管理してくれます。Unityのエディタと統合されているため、Unityのエディタから一切外に出ることなくバージョン管理ができるというのが売りです。実はUnity Asset Serverは完全に普通のPostgresSQLのサーバーをそのまま利用しているだけになっていますので、SQLを経由してバージョン管理の履歴を操作したりも出来ます。


■Unity Asset Serverを使う理由は?

なんといっても以下の3つがUnity Asset Serverの魅力でしょう。
  • 導入するのが非常に簡単。サーバー側はインストーラーをぽちぽちするだけでWindows, Mac, Linuxどの環境でも一発です。クライアント側はUnityにそのまま付属されてくるためインストールなんぞ一切不要です。Team Licenseをアクティベーションしたらすぐに使えます。
  • 運用するのが非常に簡単。管理者側もユーザー側も、バージョン管理未経験の人でもUnityエディタのGUIを経由して誰でも簡単に使用することができます。
  • 動作がそこそこ高速。バックエンドがPostgresSQLなおかげだと思うのですが、LAN環境で最大10MB/s程度の速度でコミットしたりアップデートしたりできています。これはテクスチャや音楽、3Dモデルなどの巨大なバイナリファイルを大量に扱うゲーム開発においてはかなり重要です。

■でもお高いんでしょう?

はい。お一人様5万円となっております。

お一人様、5万円です。

マジです。

なかなかボッタクリ挑戦的な価格設定だと思います。


■よかったところ

実際に2ヶ月間ほど運用してみてよかったところはこんな感じです。
  • だれでも超簡単に使えます。とにかく超簡単。これはプログラムなんて全くわからないデザイナーさんや、バージョン管理未経験の人、はたまた入社2日目の人でもなんの問題もなく使えている事実から裏付けされています。Asset Server導入前はgitを使っていたのですが、GUIクライアント付きでもトラブルだらけの毎日でした。これがAsset Serverになるやいなや全く教育もトラブルシュートもしないまま一日80コミット以上をコンスタントに記録するような素晴らしいバージョン管理体制が構築できたのです。
  • Unityのプロジェクトをバージョン管理するとき固有で発生するトラブルが一切ありません。まぁ具体的には先日書いた記事の通り、metaファイルなんですけど。前にも書きましたが、とにかくトラブルが発生しづらいです。簡単で単純なので。

■いけてないところ

さてもちろん良いところがあれば悪いところもあるわけで。以下のような点が問題点としてあげられます。
  • 高い
  • 高い
  • 高い
  • 高い
  • バージョン管理に必須とも言える機能が大幅に欠落しています。Subversion以下程度の機能しかありません。
  • 具体的にはブランチがない、タグがない、外部ツールがないのでJenkinsみたいないUnity以外から触ろうとすると超面倒くさいかPostgreSQLに直接SQL発行するしかない、基本コミットとアップデートとlog見てrevertするぐらいしかできることがないです。
  • みんなが一番期待するであろう、シーンやプレファブのマージですが、出来ません。一切できません。したがって作業範囲が衝突した時のトラブルはSubversionやgitと何ら変わりません。むしろスクリプトのマージを自動解決してくれるぶん、gitやhgのほうが優れています。
  • Unityのプロジェクト固有の問題は一切ないのですが、一般的なバージョン管理システム的にありえないバグが時々(週に1度程度)発生します。
  • 具体的にはファイルを変更したのに変更差分が認識されない(特にスクリプトを外部ツールでマージした際に顕著に発生)、updateしたのにローカルのファイルが古いまま、Historyから復元しようとしたら復元元のディレクトリが無いとか言われて復元できなくて死亡して愚痴愚痴愚痴愚痴愚痴...
  • 大人数で使うことを開発元が想定していないフシが多々あって、一番わかり易い例が最大同時接続数。これ最初の設定だと20人が限界です。PostgresのデフォルトがTCP接続40本までで、Asset Server Clientは1クライアントあたり2本TCPを貼るので。更に悪いことにこの設定を変更するにはPostgresSQLの設定ファイルを自分で操作する必要があります。簡単だからという理由で導入してここで躓いたチームは地獄を見る(むしろほぼ解決不可能になる)ことになります。
  • ところどころ完成度の低い機能があったりします。例えば既存リポジトリのコピーが可能なんですが、誰か一人でもどこかのリポジトリにクライアントが接続していたらコピーが出来ないと言われてしまいます。・・・えーと、全員帰った後にコピーしてくださいってことでしょうか><

■結論

Unity Asset Serverは、状況次第では非常に強力な選択肢になりえます。例えばチームメンバーのほとんどがバージョン管理システムを使ったことがないとか、グラフィックやサウンド担当の人が多くて彼らがSubversionやgitを覚えるのは不可能というとき、社内でSubversionやgitのサーバーを運用するノウハウがない少人数のチームのとき。こういう時にはAsset Serverの導入しさえすれば誰でも使える簡単さが大いに役立ちます。

しかしながらこの内容で1ユーザー5万円という値段はどう考えても暴利と言わざるを得ません。またできることが浅く、機能が少なすぎる弊害が簡単さによる生産性向上度をだんだん上回っていきます。とくにJenkinsと組み合わせる際の相性の悪さが辛いです。シーンやプレファブのマージができるのであればまだ5万円の価値はあったのですが・・・ということで、すでに他のバージョン管理システムを使えているチームでしたら、ぶっちゃけそのバージョン管理システムを使ったほうが良いと思います。


■こぼれ話

Unity Asia Bootcamp Tour東京で聞いた話など。まずGREE(というか@splhackさんの部署)はバージョン管理にgithubを使っているそうです。gitでは無理だと思っていたのですが、チーム全体の習熟度が高ければ問題なく回せるものなのかと感動しました。それからUnity Japanの大前さんにUnity Asset Serverに関する不満を申し上げたところ、やはりUnity側でもそのあたりは問題点として認識されているとのことでした。改善に期待するしかないですね><

2012年4月15日日曜日

iphone_dev_jp 東京iPhone/Mac勉強会でしゃべってきた & 反省

昨日開催されましたiphone_dev_jp 東京iPhone/Mac勉強会で1セッションほどお話しさせて頂きました。
http://atnd.org/events/26946

当日の様子がニコ生で配信されていて、現在タイムシフトで視聴することができます。一週間視聴できるので、4/20までですかね。
http://live.nicovideo.jp/watch/lv88663921

このタイムシフト視聴であとから録画されたプレゼンが見られるというのが実に素晴らしくて、具体的には以下のようなご利益があるわけです。
  • 遠隔地の人(今回は大阪の人)が同時に勉強会に参加できる
  • 参加できなかった人があとから当日の雰囲気を見るのに使える
  • 発表者があとから自分のプレゼンを見て反省するのに使える
主催していただいた岸川さん、機材を準備していただいたshachiさんをはじめ運営の方々、ありがとうございます!この仕組が他の勉強会でも流行ればなぁとか思ってます。(自分で主催する気がなくてすんません><)

さてここからは反省会です。タイムシフトのお陰で普段できない「自分のプレゼンはどんな具合だったか」をあとから客観的に見直すことができるので、これは大いに捗るぞと思いちょっと見てみました。2時間30分ちょいぐらいから出てくる一番煩いのが私なのですが。

うん。これはひどい><
ともかく、悪かったところとまぁ良かったんじゃないってところを列挙してみます。
  • 喋るの早すぎ。テンションが上がると更に早くなって困る。
  • 落ち着きなさすぎ。動きがなさすぎるのもつまらないので動くの自体は良いと思っているけれども、動きに変な癖があってそれが煩い。
  • プレゼンの資料で前提知識というか説明が抜け落ちている箇所が多々ある。急ぎで作ったBlocksの資料とかが顕著で、なぜ最初にアプリをクラッシュさせてるのとか説明がない。唐突すぎる。
  • 超テンションで超フランクにやったおかげかツッコミがいただけた、ネタ議論っぽくなったのはすごく良かった。だが、うまく広げられてないのがもったいない。テンパりすぎ。
逆にプレゼンが非常にうまかったsonsonさんやfladdictさんのプレゼンを見て、自分のプレゼンと比較して気づいたところを列挙してみます。
  • 発表に安心感というか安定感というか落ち着きがある。このあたりは場慣れしていきたい。
  • 図の使い方、図による説明の仕方が上手い。直前まで作っていたとか手書きで仕上げたのはやっぱダメですね><
  • デモの回数・実施方法が適切。最もキモとなる1回か2回程度に抑え、あまり資料から離れないため、流れが中断されない。ちょっと自分のプレゼンはコードとデモに逃げすぎたため流れが悪くなっていた。必要であればコードは資料の方に書いておいて流れを切らないようにする(要するに手抜き資料を作ってはならない)

あとはこれを踏まえてどんどん場数を踏んで上達していきたいですね。ということでiOSの勉強会とかで発表者募集している方がいらっしゃいましたら是非宜しければ喋らせてください!

2012年3月17日土曜日

非同期で動作する OCUnit (SenTestingKit) を書いてみた

非同期のテストができないのでGHUnitを使っていたのですが、やっぱりCmd+U一発で単体テストが走る便利さがいいなーと思い、ためしにSenTestingKitで非同期のテストができないかやってみたらできちゃったので公開します。

https://github.com/akisute/SenAsyncTestCase

ライセンスはMITライセンスとします。

使い方とか例とかはREADMEを見たりテストケースを見てみたりしていただければ一発でわかるかと思います。あ、もちろんSenTestingKit.frameworkが必要ですよ。

2012年3月4日日曜日

Unity 3.5でバージョン管理をする


およそコンピュータを使った仕事に該当するものでしたら何でも、複数の人物が同時に作業するための何らかの仕組みが必要になるのは、コンピュータを使った仕事に関わっている人でしたら皆ご存知かと思います。特に開発業務といえばgitやhgといったバージョン管理システムの出番になります。そういうわけで今回は Unity プロジェクトでのバージョン管理について一ヶ月間ほどやってみた結果をご紹介したいと思います。


■Unity 3.4以前の場合

そんな暗黒時代のことは忘れましょう。


■Unity 3.5以降の場合

Unity 3.5から新しい仕組みが導入され、 Unity Asset Server というビルトインの ボッタクリ バージョン管理システムを使う以外に、好きなバージョン管理システムを使ってUnityのプロジェクトをバージョン管理することができるようになりました。これを使わない手はありません。まずは公式のガイドを見てみましょう。
http://unity3d.com/support/documentation/Manual/ExternalVersionControlSystemSupport.html

これによると、以下の手順を踏むのが良いようです。
  1. メニューの Edit -> Project Settings -> Editor から、 Version Control の Mode という項目を探し、 Metafiles を選択する。これで外部のバージョン管理システムが導入できるようになります。このチェックを入れると、
    • Libraryディレクトリが重要なメタデータを管理するディレクトリではなくただのキャッシュディレクトリになり、バージョン管理をしなくてよくなる。
    • 新たにAssetsディレクトリ以下のすべてのファイルとディレクトリについて.metaファイルが生成される。
    • 新たにProjectSettingsディレクトリが生成される。
  2. メニューの Edit -> Project Settings -> Editor から、 Asset Serialization の Mode という項目を探し、 Force Text を選択する。この手順はどちらかと言うとおまけです。これを実行すると、ほとんどのAssetファイルがバイナリではなくテキスト形式で保存されるようになり、diffやmergeが行えるだけではなく差分だけをコミットするようになるためバージョン管理システムが扱うデータ量が減ります。
  3. プロジェクト配下のAssetsディレクトリとProjectSettingsディレクトリをバージョン管理システム配下に追加する。それ以外のファイルは追加しなくて良い。
たったのこれだけです。なかなか簡単そうですね!ということで早速やってみました。

・・・ところがぎっちょん、実際にやってみると出るわ出るわ問題の山。


■.metaファイルがどこからともなく勝手に生成される

これが最大の問題です。外部バージョン管理をするためにUnityが.metaという名前のメタファイルをテキスト形式で生成するのですが、コイツがトラブルを起こしまくります。
  • ファイル削除時に.metaを消すのを忘れて、同じ名前のファイルをあとから作成しようとして.metaがぶつかりトラブルになる
  • ファイルも.metaもバージョン管理下から確かに消したはずなのに、なぜか.metaファイルだけが勝手に復活している
  • ブランチを移動すると急に.metaファイルが湧いてきて、ブランチのマージ時に衝突する
主要な原因はUnityが空のディレクトリから.metaファイルを生成しているためです。 以下のようなケースで、中身が空のディレクトリというのは容易に生成されてしまいます。
gitの場合
gitはディレクトリをバージョン管理対象に含めないため厄介なことになります。通常はgit pull時にmerge/rebaseが実行され、その際に空になったディレクトリは削除されるのですが、merge/rebaseの最中に衝突が発生したりすると問題が大きくなります。merge中に裏でUnityが動いていると空ディレクトリの.metaがその場で生成されて、考えるだけでも恐ろしいことになったりします。
通常はこれで問題ないのですが、merge/rebase時に衝突が発生していて、裏でUnityが動いていたりすると酷いことになるみたいです。
hgの場合
hgはgitと同様にディレクトリがバージョン管理対象外なのですが、hg update時に空になったディレクトリをきちんと削除してくれます。これはupdateがworking directoryの中身を指定されたリビジョンの状態にするから、という認識です。gitと違いpullやupdate時にmerge/rebaseを実行しないためトラブルになりづらいのか?などと考えてますが実際に苦労したわけではないのでよくわかりません。おそらくgitよりはトラブルになりづらいとは思います。
1および3にupdateすると、きちんとFolderやOtherFolderが削除されてくれます。いい感じ。
svnの場合
ディレクトリもバージョン管理の対象にするので問題ありません。以下の図を参照。
r2のような削除の仕方をするとFolderが残ってしまうためFolder.metaが生成されてしまうのですが、普通はディレクトリを消したときはr4のような削除の仕方になるので問題がないはずです。
また中身が事実上空なディレクトリ(すでに使われていない.metaしか入っていないディレクトリなど)が亡霊みたいにさまよっていて延々と自身の.metaを書き出し続けたり、善意で予め作っておいた空ディレクトリが.metaを吐き出していることを知らずコミットされては削除されを繰り返していたり、二人が同時にディレクトリを削除しようとして片方のコミットが衝突して結果が壊れてしまったりなど、枚挙に暇がありません。

元ファイルを移動したり削除した際に.metaを消さなくてはいけないというだけでも極めて面倒で、相当なトラブルになりました。そもそもがUnityを使っているプロジェクトのメンバーが全員バージョン管理システムに慣れていないため、とりあえずバージョン管理システムのGUIツールがいうがままに変更を全部コミットするだけという具合の運用に。.metaだけが延々と残って衝突が繰り返されるという悲惨な状況が発生してしまうこととなりました。これはUnityのせいではなくメンバーがバージョン管理の考え方を正しく理解していないのが問題なのでツールは何を使っても発生しうるのですが、.metaがなければここまで問題は拡大しなかったと思います。

いっそignoreしてしまいたいぐらいなのですが、この.metaにファイルごとのメタ情報が全て詰まっているため、当然ignoreするとプロジェクトがブッ壊れます。これはひどい><

本家のドキュメントで例にされているのがgitでもなくhgでもなくsvnであるあたりを見ると、なんだかsvnで動作させることしか考えてなさそうな印象です・・・


■.unityファイルがマージできない

.unityファイル自体はテキスト形式にできるのですが、中で使われているAssetのIDがちょっと触れただけでものすごい勢いで変化したり、他人の環境では別の値になったりするため、殆どの場合マージできません。iOSプロジェクトのxcodeprojファイルみたいな感じです。こればかりはマージを諦めるしかありません。


■まとめ

最初はgitを使って管理をしていたのですが、上記に挙げたような問題が多発しまくって回らなくなってしまいました。別のバージョン管理システムを使うことも検討したのですが、そもそもメンバーのバージョン管理に対する知識が足りていない上に.metaが存在する以上hgやsvnに変えても問題が根本的に解決しそうにないと判断し、結局社長に頼んでAsset Serverを購入して試してみることになりました。がっかしです>< ですがAsset Serverならば.metaを使わないでバージョン管理できるので問題を根本的に解決できそうです。

逆に言えば.metaに気をつければそれ以外の箇所ではgitでもあまり問題になりませんでした。バイナリを大量に扱うため重いのではないかと懸念されていましたが、github経由ででも問題なくpush/pull出来る程度の重さにしかならなかったです。ということで.metaとうまく付き合えるチームメンバーが揃っているのであれば、Asset Serverなしでも十分やっていけるのではないかと思いました。

個人的にUnityでバージョン管理をする際におすすめする外部バージョン管理システムはsvn、可能であればPerforceです。git/hgのようにブランチを主体とするバージョン管理システムはブランチを切り替えた瞬間に.metaに殺されるケースが多々あるため、ブランチの使用そのものが非推奨となり、力が発揮できません。mergeについてもバイナリファイルやmergeできないテキスト形式のファイルが多くて結局意味がない気がします。どうしても分散VCSを使いたい!というならhgをお勧めします。ブランチ切り替えは常にupdate -Cすることで対応できるかなと。bazaarはわからないのでノーコメント。gitはpull時の衝突の最中に裏でUnityが動いていて大爆発するケースがあったのでやめておいたほうが良いです。hgなら少なくともpull/update時に爆発することはないはずですからね。

次はAsset Serverを試してみて、使用感を書いてみようかと思います。果たしてお値段に見合った効果は得られるのか!?乞うご期待です!

スタイラスペンの先端を保護する画期的な方法を思いついた


iPadなどのタブレット端末向けにスタイラスペンを持ち歩いていると、先端が破損してしまったり書き味が悪くなってしまったということはありませんか?最近のスタイラスペンの先端は繊細なシリコンゴムでできているものが主流で、持ち運びの最中に強い力がかかったりしてしまうと書き味が悪くなってしまうのです。そこで今回ご紹介したいのが、 Bamboo Stylusにマジックラッションペン No.300のキャップがジャストフィットする ってことなんです。こうやって写真のようにラッションペンのキャップをBamboo Stylusにつけてしまえば、持ち運んでいる最中にペン先が痛むことがありません!

Bamboo Stylusはクリップが取り外せるので、書いている最中はこんなふうに後ろにキャップが付けられます。


これで随分とペン先の劣化に悩まされることがなくなりました。これから発売される高級スタイラスは先端保護のためにキャップを付けて欲しいなぁと思った次第です。



■以下完全に余談: こんなことを思いついた経緯

もともと私は以前からiPad向けのスタイラスマニアで、よさそうな新製品が出るたびに買っては買っては試し、気に入ったものはポケットに入れて常に持ち歩くようにしています。
参考: http://akisute.com/search?q=スタイラス

その中でも特に書き味が素晴らしく、気に入っているのがBamboo StylusとSu-Penです。

B004XF0FQWWacom iPad/IPad2/iPhone4対応 描画、ポインティングに最適なタッチペン Bamboo Stylus CS-100/K0
ワコム 2011-05-27

by G-Tools
B005VCL6AKiPad/iPhone用スタイラスペン Su-Pen P101M-AS
7knowledge

by G-Tools

しかしながらこの2本はどうも先端の耐久性が他の製品にくらべて今ひとつなのか、ポケットに入れて持ち歩いているとどんどんペン先が劣化してしまうのです。まず最初にSu-Penが一月程で書けなくなってしまいました。TwitterでぼやいていたらSu-Penの担当の方に迅速かつ丁寧な対応をいただき、初期不良交換までしていただいたのですが、新しいペン先もやっぱり一月でダメになってしまいました。ものすごく丁寧な対応をしていただいたのになんかすみません>< Bamboo Stylusも三ヶ月ほどでペン先が死んでしまうため、こちらは購入後ペン先を2回も交換しています。これは悲しい。

そんな具合でスタイラスのペン先の劣化に悩まされていたある日、会社に落ちていた鉛筆と鉛筆キャップを見てふと思いついたのです。 そうだキャップを被せれば良いじゃん と。よくよく考えたら市販のペンは全てペン先にキャップが付いているかノック式になっていてペン先が収納できるようになっているんですよね。どうしてスタイラスにペンキャップがないのかと。早速東急ハンズの文房具コーナーにダッシュしてそこら中のペンのキャップを引剥して Bamboo Stylusに付けてみました。その結果たったひとつだけBamboo Stylusにジャストフィットするペンキャップが見つかったのです。それがマジックラッションペン No.300!

B000W9FK5Y寺西化学工業 マジックラッションペン No.300 黒
寺西化学工業

by G-Tools

まさかこんな所で1960年代から続くロングセラー製品と最先端技術が融合するとは!

2012年2月9日木曜日

ゲーム作り始めました



というわけで BP辞めちゃった わけなんですが、じゃあお前今なにやってんのよというのがこちらです。
http://www.gamecast-blog.com/archives/65644888.html
http://d.hatena.ne.jp/hotmiyacchi/20120204/1328376479

はい。ゲーム作り始めました。

なんでやねん!!!

これにはまぁ深いワケがあるのですが、話は去年の11月ぐらいまでさかのぼります。この新会社の社長さんの 宮川さん という方と以前から個人的に交流があったのですが、ある日ちょっと話したいことがあるから来てくれと飲みに誘われまして。その席で、

「君のコードを見させてもらった。大変素晴らしい。実はこうこうこういう仕事をやることになって、君の力が必要だ!是非来てくれ!!」 (会話内容はだいぶ事実と異なります)

とラブコールをいただきまして。
で、実は宮川さんって
http://www13.atwiki.jp/game_staff/pages/125.html
聖剣伝説3とかFF11とかのメインプログラマやってるわけですよ。でまぁ私、聖剣3とか全キャラのエンディング見るまでやりこんだわけですよ。そんなゲームの作者の人に来てくれ!なんて直接呼ばれた日にはもう、そんなんホイホイついていくに決まってるじゃないですか。ってことで転職することになったわけです。あとは受託開発じゃなくて自社の製品を出していけるというのも決め手でした。iOS向けで自社の製品を出していける、それも冗談みたいなのじゃなくて普通に市場のトップを取れる可能性がある製品が出せるかもしれない、というのはものすごく魅力的に見えたのです。


■転職してからこれまでのあらすじ

ぶっちゃけると私はこの転職、最初ものすごく不安だったのです。というのも
  • ゲームとかマジ作ったことない
  • 3Dプログラミングとか一ミリもわからない、むしろ数学とかマジわからない
  • ビープラウドがあまりにも良い環境だったし、給料もたくさんいただけたし、仲間も面白かったし、毎日勉強になりまくってたしで、正直転職先がこんなに恵まれた環境になるとは到底思えない
  • 宮川さんのはてダ を見ていただければわかるのですが、基本的に彼はやたらとBlogに書く内容に誇張表現が多いので なんか嘘っぽい
まぁ、やると決めたし、これまで二年間ほどBPに甘えさせていただいて楽して成長できてたし、独り立ちして苦労するのも悪くないか、などと自分に言い聞かせて2/1の初出社を迎えたわけですよ。

そしたらたまげた。 新しい職場にはBPより頭がおかしい連中しかいなかったんですよ。

冗談じゃなくこんな感じ

その日集まった18人全員のスキルがどいつもこいつもXMENだったと。当日の様子をお伝えできないのが非常に残念なのですが、一体全体宮川さんはこんな連中を日本のどこから集めてきやがった!!と叫ばざるを得ない。そしてまた彼のマネジメントのやり方が驚嘆するほどに上手いのです。以下ハイライト。
  • いきなり初日からクライマックス状態の自己紹介タイム
  • いきなり初日から会社でふぐちり鍋(注: 水曜日です)
  • でも翌日朝9時出社は厳守させる、無断欠勤してきた奴にはものすごい勢いで怒る→全員超引き締まる
  • いきなり2日目から「1日に1本ゲームの企画を立てて作る、4日連続で、チームメンバーは毎日全員入れ替え」という頭おかしい研修内容
  • それを当たり前のようにクリアする全員、当然のようにiOS実機で動く
  • それどころか毎日完成するゲームの質が上がる
  • それどころか毎日完成したゲームのプレゼンの質が上がる
  • 4日終了した段階で、全員の打ち解け度合いが半端ない、よくある「チームごとの壁」みたいなのが全くない状態で実際の仕事に突入
  • 研修が終わった日にまた鍋(注: 一週間に二回鍋です、火曜日です、終電まで全員飲み食いしてます)
  • でも翌日朝9時出社に誰も遅刻しない
  • グラフィッカーさんに良いマウスがないか聞かれたのでSteelSeries XAISteelSeries 4HDの実物を持っていって触ってもらったら大絶賛され、翌日Xaiと4HDがそれぞれ8個ずつ届いた(注: 1セット1万2000円ぐらいします、たかがマウスです)
  • 無線LANアクセスポイントの調子がわるいのでCISCOの良い奴にしましょうと答えたら、二日後に本当にCISCOのAPが届いた
  • 開発用のMacツールおよび企画研究用のiOSゲームは全て買い放題
  • 昼休みに社員8人程度でHaro Reachのデスマッチをプレイしてる
  • 仕事上がりに同僚とCoDのcoopをプレイしてる
  • 同僚にシャドウゲイトの話が通じる
  • 同僚にPortal2やSkyrimやStarCraft2の話が普通に通じる
などなど、枚挙に暇がありません。いや、正直最初はFF11のメインプログラマやってたとかいうけどどうなのかなーなどとものすごいタカをくくっていたのですが、ほんとスミマセン。これは本気で強いです。バラ色天国な環境過ぎて、これはもう勝ったと言わざるを得ない。前職のBPも素晴らしい環境だったのですが、ここの環境は正直常識が通用しないレベルですごいです。日本で一番すごい環境じゃないかと言われたらイェスと答えてもいいかもしれないです。

そして環境も素晴らしいですが同僚も皆とんでもない連中ばかり。スキルは当然として、全員が向学心と負けず嫌いの塊みたいなものですから、研修がとんでもなく白熱しまして、正直ヤバい。私もBPのハッカー魂と業務改善の魂を是非新しい職場に吹きこんでいこうと日々頑張っております。当分の間は退屈しない、全力全開の日々が過ごせそうです。

2012年2月8日水曜日

ビープラウドを退職しました


やや旧聞になりますが、1月末付で 株式会社ビープラウド を退職いたしました。知ってた人はすでに知ってたと思うんですが、なんかすっごい唐突で色々とすみません>< 退職理由や次の仕事の話などは次の投稿に詳しく書く予定ですのでそちらに任せるとして、今回はこの場をお借りしましてビープラウドがどういう会社だったか、ちょっと過去の振り返りなどをしてみたいと思います。


■BPはどんな会社か

(良い意味で)頭おかしい連中ばっかりが集まって、本気で学び本気でコード書いている集団だと思います。

具体的には 右隣の奴 が常時こんな状態で仕事してます。

こんなことを書くと本当に変人しかいないんじゃないのか、お前ら大丈夫か、などと思われてしまうのですが、そうじゃないんです。ビープラウドはみんなが自分のすべてをぶつけて、さらけ出して仕事できる素晴らしい会社なんです。私は新卒で入社したSIerの会社をやめてBPに入社したのですが、入社した初日にパスワードが失われてログイン出来ないMacを渡されて何とかハックして使えという具合だったり、それまで同期の誰に喋っても通じない技術の話が当たり前のように通じたり、っていうかむしろ私が知らない事のほうがはるかに多かったりで、全力で自分の技術をぶつけることができた感動を今でも覚えています。世の中にはいろんなくだらない理由だとか社内政治だとかで、自分の全力、技術力のすべてを出し尽くせない、出してはならない、出す価値もないような仕事が散見される(それが最初の会社をやめた理由の一つでもある)のですが、ビープラウドでは全力全開超推奨。二年間で vの人 から「お前は随分良くなった」とお褒めいただけた程度には成長できたのも、間違いなくビープラウドで自分の全力を出して挑戦し続けさせていただいたおかげだと思っています。

私が見たビープラウドの感想なので、他の人から見たらちょいとズレているのかもしれませんが、私は技術を大事にする、エンジニアを大事にして全員にMacやらレッドブルやらを支給する、社内勉強会やBPStudyを開催する、などといったBPのハッカー文化が大好きで、退社した今でもその気持ちは変わりありません。むしろ退職して新しい仕事をしている今から振り返ると、BPの素晴らしかったところはあのハッカー文化だったんだなという思いを強くしています。


■謝辞

去年の12月にいきなり辞めると言い出したにも関わらず快く送り出していただいた社長のはるおさんとCTO、超ムチャぶりにも関わらず私の仕事を引き継いで立派に締めてくれたgkt、個人的に超いろいろ迷惑をかけまくった右隣の席の奴に、ありがとう!!むしろBP全員ありがとう!!!お陰様であきすては元気にやってますよ!

2012年1月29日日曜日

xxd を使って画像などのバイナリデータをソースコードに含める方法

iOS向けのライブラリやフレームワークを作成しているときに、どうしても画像などのバイナリデータをライブラリやフレームワークに含めたくなる時があります。たとえばUI系のフレームワークなどですね。このようなときに、たとえば静的ライブラリ(.aと.h)やフレームワーク(.framework)とセットで画像を一緒に同梱し、ユーザーのXcodeプロジェクトに一緒に含めてもらうという方法もあるのですが、この方法だと画像名がユーザーのプロジェクトに含まれている画像とかぶったりしてはいけませんし、管理が面倒になってしまいます。また、ライセンスがプロプライエタリなライブラリでは、画像などのリソースをあまり積極的にユーザーに公開したくないというニーズがあったりします。

そこでxxdツールのご紹介です。岸川先生に教えていただいたのですが、xxdというツールを使えばバイナリデータをC言語のヘッダファイルとして簡単に出力することができるらしいのです。これを使ってバイナリデータをライブラリ内部のソースコードの一部として配布してみましょう。

xxdはvimに同梱されているので、最初からMac OS Xについてきます。使い方も非常に簡単です。
xxd -i Sample.png
とすると、
unsigned char Sample_png[] = {
  0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
  0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x07, 0xfd, 0x00, 0x00, 0x04, 0x73,
//中略
unsigned int Sample_png_len = 589903;
このような感じでバイナリデータがC言語のヘッダファイルになって出力されます。あとはこれをNSDataにして、UIImageを生成することができます。NSDataを作る際には余計なデータコピーが発生しないdataWithBytesNoCopy:length:freeWhenDone:を使うことをお勧めします。
NSData *data_Sample_png = [NSData dataWithBytesNoCopy:Sample_png length:Sample_png_len freeWhenDone:NO];
UIImage *image = [UIImage imageWithData:data_Sample_png];

2012年1月24日火曜日

Jenkins を iOS アプリ開発に導入してみた (gcov編)


前回 はSenTestKitに続いてGHUnitを導入して、Jenkins上でGHUnitによるテストの自動実行を行いました。今回はさらにステップアップして、gcovを使用してコードカバレッジを取るようにしてみようかと思います。


■gcovを使ったコードカバレッジの取得(Xcode 4.3版)

Xcode 4.3の場合は以下の参考ページで紹介されている方法がオススメです。実機ででもシミュレータでも動作してお得です。
http://www.cocoabuilder.com/archive/xcode/314794-xcode-4-3-moved-libprofile-rt-how-to-reference-it-now.html
http://www.infinite-loop.dk/blog/2012/02/code-coverage-and-fopen-unix2003-problems/
https://github.com/InfiniteLoopDK/ILTesting
  1. Generate Test Coverrage FilesとInstrument Program FlowにYESを指定する
  2. http://www.infinite-loop.dk/blog/2012/02/code-coverage-and-fopen-unix2003-problems/で紹介されているILProfilerCompat.cを組み込む(これによってfopen$UNIX2003によるエラーを防ぐ)
これだけで大丈夫です。


■gcovを使ったコードカバレッジの取得(Xcode 4.2版)

【2012/03/15追記】こちらの方法はXcode 4.2が前提となっており、Xcode 4.3以降は動作しません!

gcovとはgccに付属されているC言語用のカバレッジ測定ツールです。これを使うことで、C言語と、その拡張であるObjective-Cのコードカバレッジをバッチリ取得することができます。ということで早速プロジェクトに組み込んでみましょう。幸いにしてすでに先駆者の方がいらっしゃいました。
http://tech.naver.jp/blog/?p=706
基本的に上記に記載されている通りの手順で導入すれば簡単にgcovによるコードカバレッジの取得ができますが、実際にやってみたところ幾つか補足があるので追記します。

先ほど紹介した記事では/Developer/usr/lib/libprofile_rt.dylibの追加と-lgcovの指定両方を行なっているのですが、私が試したところどちらか片方だけで成功するということがわかりました。具体的には以下のとおりになります。

1. /Developer/usr/lib/libprofile_rt.dylibを使うパターン
2. -lgcovを使うパターン

それぞれ見ていきます。

1. /Developer/usr/lib/libprofile_rt.dylibを使うパターン

/Developer/usr/lib/libprofile_rt.dylibをプロジェクトに追加し、Other C Flagsの-fprofile-arcs -ftest-coverageを設定すればOKです。それ以外の手順は必要ありません。
このパターンですとスムーズにgcovの結果を出力することができましたが、残念ながらこの方法はSimulator上でしか使用できません。というのも、/Developer/usr/lib/libprofile_rt.dylibがそもそもarmv6, armv7用のバイナリを含んでいないという問題があるのと、ライブラリ探索パス"/Developer"以下がDeviceビルド時に勝手にiphoneos.sdk以下の/Developerに置換されてしまうため、ビルド時にlibprofile_rt.dylibをリンカが発見できずエラーになる問題があるためです。基本的にSenTestingKitのようなSimulatorでしか実行しないビルドターゲットでやるのが良いと思われます。

2. -lgcovを使うパターン

参考: http://stackoverflow.com/questions/5101014/code-coverage-not-showing-results-using-xcode-gcov/5140459#5140459
-lgcovをOther Linker Flagsに追加し、Generate Test Coverrage FilesとInstrument Program FlowにYESを指定すればOKです。それ以外の手順は必要ありません。
このパターンですとlibgcovはarmv6, armv7でも動作するので実機でのカバレッジ取得ができるのですが、問題はそのままの設定でビルドして実行すると実行時にクラッシュします。これはlibgcovが実行時にカバレッジデータを書きだす際に、デフォルトの設定ではiOSのサンドボックスの外にアクセスしようとするため書き出しに失敗してしまうのが原因です。したがって上記参考URLに従ってGCOV_PREFIX環境変数を使いサンドボックス内部にカバレッジデータを書きだすように指定してやる必要があります。
この方法は環境変数を実行時に設定する必要があって面倒なのと、カバレッジデータが書き出される箇所がJenkinsのビルドディレクトリの中ではなくiOSシミュレータまたは実機のサンドボックスの中になってしまうため、Jenkinsからカバレッジの取得を行うのが困難になってしまう問題があります。

今回は1. のパターンを採用し、SenTestingKit上でSimulatorビルド時のカバレッジ取得だけを行うことにしました。GHUnitでも実機上での動作を諦めれば問題ないのですが・・・まだ課題が多い感じですね。

ビルド設定ができたらビルドして、きちんとgcovコマンド経由でカバレッジが取得できていることを確認しましょう。


■gcovrを使ってCoberturaのXML形式に変換する

gcovのカバレッジデータの取得ができるようになったら、今度はJenkinsと連携させるために、gcovの出力をCoberturaというJava環境のカバレッジ計測ツールの出力するXML形式に変換する必要があります。これはJenkinsがgcovのカバレッジデータ出力の集計に対応していない為です。っていうかgcovの出力は単なるタブで区切られた文字列で人間以外にはとても優しくないので集計ができないのです。
ここで、Python製のgcovrというツールをJenkinsサーバマシンに導入します。 gcovrをgcovの代わりに使うことで、CoberturaのXML形式で結果を出力できるようになるほか、カバレッジ計測対象となるファイルを簡単にフィルタリングできる(たとえばテストケースクラスのカバレッジなんか集計してもしょうがないので除外するなどできる)のが大きな魅力です。
http://wiki.hudson-ci.org/pages/viewpage.action?pageId=45482230
https://software.sandia.gov/trac/fast/wiki/gcovr

インストールはMacマシンであれば非常に簡単で、以下のコマンドを打つだけです。
sudo easy_install gcovr
インストールが完了したら、あとは
gcovr --xml --output=cobertura-report.xml build/
とかやれば一発でbuildディレクトリ配下のすべてのカバレッジを計測結果を集計してXML形式で書きだしてくれます。
ここでもし、テストケースのカバレッジは除外したいなどという要件がある場合は、
gcovr --xml --exclude=".*/.*Test(s)?\.[(c)(cpp)(m)(mm)]" --output="cobertura-report.xml" build/
などとすればOKです。


■JenkinsのCoberturaプラグインを使って結果を集約する

CoberturaのXML形式で結果が取れるようになってしまえばあとは楽勝です。JenkinsにCoberturaプラグインをサクっとインストールして、
https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin
あとは以下のようにJenkinsのジョブ設定を書いてしまうだけです。先述の通り、SenTestingKit上でSimulatorビルド時のカバレッジ取得だけを行いたいので、既存のSenTestingKit用単体テストジョブのXcodeビルドの後でgcovrを実行するシェルスクリプトを実行してやれば一発です。


カバレッジが取れるようになると断然見栄えがしますね。

2012年1月23日月曜日

Jenkins を iOS アプリ開発に導入してみた (GHUnit編)


前回 はSenTestKitを用いてJenkins上で単体テストの自動実行を行いました。今回はGHUnitを使った単体テストの自動実行にチャレンジしてみたいと思います。またついでといっては何ですが、単体テスト時に必要になってくるモックを作成するためのライブラリOCMockも同時に導入してみようと思います。


■なぜGHUnitを使うのか

GHUnitを使うことで、SenTestingKitと比べて以下のようなメリットが得られます。
  • 非同期処理のテストを行うための仕組みが用意されている(GHAsyncTestCase)これをSenTestingKitないし他のテスティングフレームワークでやろうとすると大変骨が折れます。
  • .app形式(要するに実際のiOSアプリケーション)でテストを実行するため、UIApplicationやUIWindowといったUIコンポーネントを使うクラスのテストが可能になる。UIテストの支援をするための仕組みも最近のGHUnitには追加されている。
    • SenTestingKitの"Logic Test"では想定通りに動作しないUIKitやFoundationのクラスが幾つかあり、中には純粋なロジックで使いそうなクラスも含まれている。そのためSenTestingKitではテストできないロジッククラスが発生する場合があるので、そういう時はGHUnitを使うと良い。
    • 参考: https://gist.github.com/1662887
  • 実機上でテストを実行できる。そのため実機でのみ発生するバグをつかまえることが可能。
  • SenTestingKitのテストケースをそのまま実行できるため上位互換として使用できる。
逆にGHUnitにはSenTestingKitと比べて以下のようなデメリットがあります。
  • Xcode 4で統合されたUnitTestingの恩恵に預かれない。すなわちCmd+U一発でテストケースを実行することができない。Xcode経由だとCmd+Rでアプリケーションとして起動して、その後起動したアプリのテスト実行ボタンを押す手間が必要になるため、開発のテンポが乱されてしまう。特にTDDを採用している場合には深刻。
  • 上記問題を回避するためCUI経由でテストを実行することもできるのだが、Xcodeのエディタと連携しないため、どこがエラーになっているのかが一目でわかりづらい。
  • カバレッジの取得が楽。SenTestingKitをシミュレータ上で実行している時が一番やりやすい。これについては別の記事で解説します。
これを踏まえると、以下のように使い分けができるようになります。
  • SenTestingKitは純粋なロジックの単体テストを実行したいときに良い。Xcodeからすぐに実行できて一瞬で結果がコード上に出るので、開発のテンポを乱さない。TDDに向いている。
  • GHUnitは非同期なAPIを持ったクラスの単体テストを実行したいときに良い。またUIApplication, UIWindowといったUIコンポーネントと結合したり、通信やファイルアクセスなどの外部リソースとの結合をおこなった状態でのテストを作る際にも向いている(純粋な単体テストともユーザー受け入れテストとも異なるため、仮に結合テストと呼ぶことにする)ので、そういったものが必要な場合には最高の選択肢となる。
プロジェクトの開発方針や開発対象によってどのようなテストが必要になるのかが異なってくると思いますが、どれか1つだけ必要であればGHUnitを選択し、きちんとしたXPでやりたいプロジェクトはSenTestingKitに単体テストをやらせ、ユーザー受け入れテストに KIF などを採用し、その中間を埋める必要があればGHUnitも導入する、というのが良いのではないかと考えています。ちょっと面倒ですけど。


■GHUnitを導入する

以下のリポジトリからコードを取得して、XcodeからビルドすればOKです。
https://github.com/gabriel/gh-unit
注意点として、GHUnitは.framework版がそのままgithubからダウンロードできるのですが、
https://github.com/gabriel/gh-unit/issues/69
このような問題が報告されており使えないので、githubからソースコードをクローンしてきて、
cd Project-iOS
make
でビルドしてできたGHUnitIOS.frameworkを使うようにしてください。

frameworkをビルドしたら自分のプロジェクトに追加して、GHUnit用のビルドターゲットを作ります。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_install_ios_4.html

ビルドターゲットができたら、次は試しにテストケースを追加してみて、問題なくテストが走るか確認。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_testing.html

最後にCUI(要するにxcodebuildコマンド経由)でビルドができるようにします。これをやらないとJenkinsと連携できません。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_command_line.html


■OCMockを導入する

以下のリポジトリからコードを取得して、XcodeからビルドすればOKです。
https://github.com/erikdoe/ocmock
iOS向けビルドはスタティックライブラリ用のビルドしか用意されていませんので、どうしてもフレームワークが良いとこだわる人は頑張ってください>< そうでなければ非常に簡単です。ビルドしたら成果物をそのままプロジェクトに突っ込めばすぐ使えるようになります。
OCMock自体の使い方にはここでは触れませんが、例えば以下のようなテストケースが作れたりします。

- (void)testOCMock
{
    // Creating a new stub object from class
    {
        id mockedObject = [OCMockObject mockForClass:[NSString class]];
        STAssertThrows([mockedObject length], @"Mocked object raises exception because the fake method is not ready yet.");
        STAssertThrows([mockedObject isKindOfClass:[NSString class]] , @"Mocked object can't even call isKindOfClass: because it's not ready.");
        STAssertFalse([mockedObject class] == [NSString class], @"Mocked object is not a kind of the target class.");

        [[[mockedObject stub] andCall:@selector(mockedLength) onObject:self] length];
        STAssertEquals([mockedObject length], (NSUInteger)100, @"Mocked object returns the fake value.");

        [[[mockedObject stub] andReturn:@"AllYourBaseAreBelongToUs"] lowercaseString];
        STAssertEqualObjects([mockedObject lowercaseString], @"AllYourBaseAreBelongToUs", @"Returns mocked value.");
        STAssertEquals([mockedObject length], (NSUInteger)100, @"Previously mocked methods are still valid.");
    }

    // Method Stubbing for existing object
    {
        NSString *stubTargetObject = @"I am a stub target.";
        STAssertEquals([stubTargetObject length], (NSUInteger)19, @"The original implementation of the length method.");

        id mockedObject = [OCMockObject partialMockForObject:stubTargetObject];
        STAssertTrue([mockedObject isKindOfClass:[NSString class]] , @"Mocked object is a kind of the target class.");
        STAssertEquals([mockedObject length], (NSUInteger)19, @"Mocked object returns the original value.");

        [[[mockedObject stub] andCall:@selector(mockedLength) onObject:self] length];
        STAssertEquals([mockedObject length], (NSUInteger)100, @"Mocked object returns the fake value.");
        STAssertEquals([stubTargetObject length], (NSUInteger)100, @"Stubbed target object also returns the fake value.");
    }
}
- (NSUInteger)mockedLength
{
    return 100;
}


■Jenkinsと連携させる

最後にJenkins側でビルドターゲットを作りましょう。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_ci.html

こんな感じの設定になると思います。

あとはいつもどおりジョブを回してみて問題がないか確認すれば完了です。前回ご紹介したようなSenTestKitの単体テストジョブがうまく回っていれば、それをコピーしてきてちょっと設定を変えれば簡単にできるとおもいます。

2012年1月21日土曜日

Objective-C がこの四年間でどれぐらい進化したのか一目でわかるテストケース

Twitterに流したら思ったよりも好評でしたので、ブログにも上げておきます。

こちらがiOS 2地点でのNSURLConnectionクラスを使った非同期通信のテストケース。

こちらがiOS 5でのNSURLConnectionクラスを使った非同期通信のテストケース。

Blocksはやっぱり偉大です。一つしかテストケースがないうちはまだマシなのですが、これが10個とかになると楽さが全く違ってきます。ぜひためしにURLだけ変えて同じテストケースを10個作ってみてください。iOS 5のBlocksを使ったコードはほとんどコピペだけで終わりますが、iOS 2でのdelegateを使ったコードは他にも変更しなければならない点が多数出てくるはずです。

また実際にこのコードを走らせてみると、理由はよくわからないのですがiOS 5で追加されたAPIを使ったコード(Blocks)のほうがそうでないコードよりも毎回2倍程度(0.1秒程度)高速に動作しているみたいです。ちょっと謎ですが、新しいAPIにはパフォーマンス面でのメリットもありそうです。GCDのおかげかな?

2012年1月20日金曜日

Jenkins を iOS アプリ開発に導入してみた (SenTestKit編)


最近、iOSアプリの開発でも継続的インテグレーション(CI)を取り入れていくプロジェクトが増加傾向にあるようで、各種ツールやライブラリ、ノウハウが出回ってきているように感じられます。そこで私も早速iOSアプリ開発でのCI導入を試してみることにしました。今回の導入試験では、以下のような環境を想定して行いました。
  • iOSアプリの開発を、Xcode 4.X系のプロジェクトとして行う。
  • VCSにはgitを採用し、githubの公開リポジトリをリポジトリサーバーとして使用する。
  • CIサーバにはMacを採用し、プロジェクトをビルドするためにXcode 4.Xをインストールしておく。


■必要なツールを準備する

CIといったら、まずは何はなくともJenkinsです。
http://jenkins-ci.org/
ここでは導入について詳しくは挙げませんが、私は以下の本を参考にしました。
https://gihyo.jp/dp/ebook/2011/978-4-7741-4952-3

続いてJenkinsのプラグインを導入します。
これでJenkinsの準備はだいたい完了です。
あとはVCSのgitですが、こちらは準備が出来ているという想定で進めます。


■余談:xcodebuildの使い方

JenkinsのXcodeプラグインを使えば、自分で面倒なantのbuild.xmlを書いたり、ビルドスクリプトを用意しなくても、JenkinsのGUI上でビルド設定を行うことが出来ますが、念のため基本を理解しておくべく、xcodebuildというツールを使ってみます。xcodebuildはXcodeプロジェクトをCUIからビルドするためのツールで、Xcodeに付属しています。JenkinsのXcodeプラグインも内部的にはこいつを使用しています。

基本的な使い方の例は以下の通り。
xcodebuild -project MyApp.xcodeproj -configuration Release -target MyApp clean build
xcodebuild -project MyApp.xcodeproj -configuration Debug -target MyAppTests -sdk iphonesimulator5.0 clean build
xcodebuild -project MyApp.xcodeproj -configuration Debug -target MyAppTests -sdk iphonesimulator clean build
上の例ではデフォルト設定のiOS SDKを用いてビルドターゲットMyAppがReleaseビルドされます。次の例ではiPhone Simulator 5.0 SDKを用いてビルドターゲットMyAppTestsがDebugビルドされます。一番下の例では、手元にある最新のiPhone Simulator SDKが使用されるようです。また -target の代わりに -scheme を使うことも出来ます。ビルドに使うことができるSDKの一覧を得るためには、
xcodebuild -showsdks
を使えばよいです。

ビルドアクションにはclean, build以外にもarchiveとかあるのですが試していないので詳細は不明です。しかし残念ながら、少なくともtestアクションがないことは確認しました。そのため、Xcode上で Cmd+U を押して実行できる単体テストが、xcodebuild経由では実行できません。これについては後ほどまた触れます。


■Xcodeプロジェクト側の準備

Jenkinsによる自動化を行うためには、まずはXcodeプロジェクト側を修正して、xcodebuildツールにより単体テストが実行できるような状態にして置かなければなりません。そのため先にXcodeプロジェクト側の設定を修正します。Xcode 4でテストケース付きの新規プロジェクトを作るとテストケース実行用のターゲットが自動的に用意されると思いますが、先述の通りこのターゲットはxcodebuild経由では実行できないため、以下の画像の様にUnit Testing -> Test Hostを削除して対応します。


このように設定を変更することで、UIWindowやUIApplicationを使わなければならないテストが実行できなくなりますが、それ以外のテストはXcodeからもxcodebuildからも実行できるようになります。試しにxcodebuildを使ってテストターゲットを実行し、テストが走っていることを確認してください。


■Jenkins側の準備(ジョブの設計)

続いていよいよJenkinsの設定に移ります。プラグインは導入してあるので、あとはジョブを作るだけです。Xcode連携のプラグインが入っていれば楽勝です。今回はシミュレータビルドで単体テストを行うので、ビルド後の処理でテスト結果の集約を行い、test-reports/*.xmlを指定しましょう。


デバイスビルドでリリース用のビルドを作る場合には、ビルド後の処理でビルド成果物の保存を行い、build/Debug-iphoneos/*.ipa(必要ならdSYMも)などを指定しておけば良いかと思います。デバイスビルドをするにはビルドをするマシンのKeychainにcodesign用の証明書と鍵が格納されている必要があるので注意してください。Technical VersionやMarketing Versionの値を指定すれば、自動的にInfo.plistに指定されているCFBundleVersionやCFBundleVersionShortStringを置換してくれるので非常に便利です。


ジョブを作ったら早速実行してみましょう。おそらくビルドだけならあまり苦労せずに通ると思います。私は10回目のビルドで完全に問題なくビルドが回りだすようになりました。

2012年1月15日日曜日

gdb で void* 型の変数をデバッグする

C言語で実装されたライブラリやアプリケーションでは、汎用的な型として随所で void* が使用されますが、これをgdbからデバッグすると、そのままでは型情報が無いためタダのポインタとして扱われてしまいます。これではデバッグ時の都合がよろしくないです。
(gdb) print 0xfee65c0
$1 = 267281856
(gdb) print (void *)0xfee65c0
$2 = (void *) 0xfee65c0
こんなとき、この void* が指し示している先の型がわかりきっている場合は、その型でキャストしてやって:
(gdb) print (struct imap_session_state_data *)0xfee65c0
$4 = (struct imap_session_state_data *) 0xfee65c0
(gdb) print $4
$5 = (struct imap_session_state_data *) 0xfee65c0
参照先にアクセスすればきちんと中身が見えます:
(gdb) print * $4
$6 = {
  imap_session = 0xfee6560,
  imap_mailbox = 0xfee6230 "INBOX",
  imap_flags_store = 0xfee6490,
  imap_ssl_callback = 0,
  imap_ssl_cb_data = 0x0
}
(gdb) print $6->imap_session
$7 = (mailimap *) 0xfee6560
(gdb) print * $7
$8 = {
  imap_response = 0xfee6090 "FETCH completed",
  imap_stream = 0xfeecc90,
  imap_progr_rate = 0,
  imap_progr_fun = 0,
  imap_stream_buffer = 0xfee6a00,
  imap_response_buffer = 0xfee6a20,
  imap_state = 3,
  imap_tag = 4,
  imap_connection_info = 0xfee64d0,
  imap_selection_info = 0xfee6030,
  imap_response_info = 0xfee60e0,
  imap_sasl = {
    sasl_conn = 0x0,
    sasl_server_fqdn = 0x0,
    sasl_login = 0x0,
    sasl_auth_name = 0x0,
    sasl_password = 0x0,
    sasl_realm = 0x0,
    sasl_secret = 0x0
  },
  imap_idle_timestamp = 0,
  imap_idle_maxdelay = 1740,
  imap_body_progress_fun = 0,
  imap_items_progress_fun = 0,
  imap_progress_context = 0x0
}
これでデバッグがはかどりました。

2012年1月2日月曜日

2011年のふりかえりなど

あけましておめでとうございます。年も開けましたので、2011年のふりかえりをやってみて、2012年の抱負を考えてみたいと思います。

■やってみたこと
2010年の途中からiOSアプリの開発担当になったのですが、2011年は始めて一年中iOSアプリの開発に携わることができました。ということでダイジェスト。
  • 1月は前年度から引き続きアプリの修正案件を行なってました。ずいぶんひどく炎上した一年のスタートになったのですが、炎上したプロジェクトでしか学べないものというのはたくさんあるものだと痛感させられました。主に案件がどうして燃えるのかとか、何が死亡フラグか、など。自分一人ではどうにもならないので、急遽助っ人に助けてもらいましてなんとか収拾。助っ人の方々、あの時は本当にありがとうございました><
  • 2月ぐらいからBPRという自社開発のフレームワークとそれを使ったアプリの開発などをしていました。外部に公開するライブラリを組むのは初めてということで、実に勉強になりました。中身の実装もなかなかうまくいったと思ってます。
  • その後ちょっとした案件をこなしてましたが、ここでは複数案件の並行進行を余儀なくされたため、またも自分一人ではどうしようもならない事態に。うまい具合にアルバイトの人に仕事をお願いしたりする必要に迫られるなどしました。
  • 5月〜6月が今年一番の正念場でした。それぐらい難しい案件と他の案件を並行で進めていたのですが、ずいぶん自分の設計からミスをしてしまい、自分の限界を知ることになった気がします。主にCore DataまわりとAPI通信実装まわりの限界がこの案件ではっきりと分かりました。それだけではなく、グループで仕事するときの死亡フラグとか、炎上案件の燃え方とか、これまた大いに学ばされました。
  • 7月からはずっと一本のアプリに集中して新規実装および修正を行なっていました。これまでの案件と打って変わってあまりにもサクサク進んだもので、受託開発での「お客さんの力量」の大事さを実感。むしろ自分のほうがお世話になりっぱなしで申し訳ない気持ちでした。この案件では試しにこれまでの社内ライブラリや設計の基本をすべて捨てて新しい方式でやってみたのですが、大いに成功したところもあればひどく失敗したところもあり、結果としてこの挑戦は正解だったと思っています。またとある理由でopensslのコードを読んだりしたなど、より低レイヤーな部分の知識が限定的ながら得られてきました。
まとめると、これまでにない難易度のアプリの開発に携われて成長できたのと、自分一人ではどうにもならない場面を何度も経験し、仲間の力の偉大さに気付かされたのが今年の収穫だったと思います。

■Keep
去年良かったので続けたかったことはこんなところ。
  • いろんな技術に手を出す。一年以上積み重ねてきたおかげで、ずいぶんと「一番いいやり方」と思われるiOSアプリの開発手法がわかってきたのですが、その方法に固執するとよりよい方法を見落としたり、時代についていけなくなると思ったのであえて別の方法を試し、結果としてよりよいライブラリやプラクティスを学ぶことができたのが実に良かったので、今年もチャレンジしていきたいところです。またClojureをvの人に薦められてやってみて、これまたずいぶんと刺激を受けたので、良いとされる言語やフレームワークに手を出してみてその設計思想を学び取るのは今年もやっていきたいですね。
  • 積極的に仲間に頼る。去年はずいぶんと仲間のみんなに助けて頂きました>< お陰様でずいぶんとスタンドプレーで無理やり解決しようとしてソウルジェムが濁るようなことがなくなったかと思います。今年も自分以外の周りに視野を広げつつ、困ったときに助けていただけるようにもっと人間的に良い人になりたいなーと思ってます><

■Problem
去年よくなかった、またはまだまだ改善の余地があるのはこんなところですかね。
  • 基礎力が足りない。低レイヤーな部分の知識が絶望的に足りない。これはPython温泉の際に指摘されたことで、実に悔しいのですがたしかに通信はHTTPより下のレイヤが殆どわかっておらず、言語はObjective-CがわかってもC/C++がわかってない。アルゴリズムの知識も足りなければ、基本的な数学知識も甘い。
  • さらなる設計力の向上が必要。ずいぶんとよくはなりましたが、それでもまだライブラリやSDKの実装のために必要なレベルの設計力がまだ不足しているように感じられます。
  • グループでの開発手法。自分一人の案件というのがほとんどだったので、たまにチーム戦をやるとひどくミスを連発して、見積もりを間違えたりレビュー不足からひどい炎上を招いたりと失敗が続いているため、グループでの開発のやり方を本気で考えなければならないと痛感させられています。
  • テストの仕方。単体テストと、結合テスト。テストの自動化事態はGHUnitのおかげでずいぶんと進んできたのですが、通信を含んだりファイル操作をするテストが単体テストに紛れ込んでいたりして、自動化の妨げとなっています。SenTestKitとモックを使ったほんとうの意味での単体テストと、それ以外のテストをきちんと分ける開発手法を編み出していきたいです。

■Try
ということでそれを踏まえて、今年はこんな感じのことに挑戦したいです。
  • より低レイヤーな知識の学習。TCP/IPとUDPは必修、できればTCP上でmsgpackあたりのRPCプロトコルを自分で実装できる程度にはなりたいと考えています。CとC++の知識も増やしていきたいです。
  • グループでの開発手法を学びたいです。あとはコミュ力向上。私は自分勝手で人の話を聞かず一度良いと感じたらその手法を信じこんでやまない(人に押し付ける傾向がある)ので、たとえその手法が実際に良かったとしても、チーム全体になじまずマイナスの影響を及ぼしている可能性があります。そういったことをなくすにはどうするか?ということで複数の開発手法を学ぶというのと、人の話を聞いてそれを採用する、これに尽きるでしょう。
  • テストの仕方の改善とCIの導入。まずは単体テストの完全自動化と、リリースビルドの日次生成からはじめ、徐々に取得する集計データの量を増やしていく、例えばカバレッジの計測は簡単に思いつきますし、GoogleやMSはバグが発生しやすい箇所を予め計算する方程式を持っているそうで、そういうのを日次で集計できれば全体の品質に寄与できる可能性があります。