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];
こんな具合でアニメーションを調整できます。


■まとめ

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