こんにちは、たんぽぽグループの森本です。
尊敬するエンジニアはカナヅチひとつで何でも作れるバイキンマンです。

前回、「ヘッドマウントディスプレイで仕事してみた」という記事でSonyのHMZ-T1というヘッドマウントディスプレイ(以下HMD)をご紹介しました。
この記事を書いた後、一ヶ月間HMDをつけて仕事をしてみました。
いろいろなメリットとデメリットがわかりましたのでそれについて記したいと思います。
またデメリットの改善点についてもお伝えします。

メリットとデメリット

メリット

  • まわりが見えないので集中できる。話しかける人も減った気がする。
  • 天井の明かりがまったく気にならない
  • 目が疲れにくい。会社帰りに外を歩いていて遠くがクッキリ見えます。(個人の感想です!)

デメリット

  • 画面が狭い
  • ヘッドフォンがつけられない(HMZ-T1のヘッドフォンはオープンエアなのでオフィスでは使えないのです)
  • 眼鏡だと画面の端がボヤけやすい
  • 重い。おでこに荷重がかかり真っ赤になる

デメリットを解消してみる

画面が狭い

これはもう慣れるしかないです。
すべてフルスクリーンにしてアプリ毎切り替えて使っています。
幸いemacs派なので、ほぼすべての作業が完結するのでそれほど不満はありません。
いつかヘッドトラッキング機能を組み込み仮想的に画面を広くする方法を試してみたいと思います。

ヘッドフォンがつけられない

もともとついているオープンエアのヘッドフォンと干渉してしまうのが原因なので気になる人は取り外してしまうのが一番です!
たんぽぽグループ周辺は電話をする人もおらず普段はとても静かなのでヘッドフォンが使えなくてもそれほど不満はありません。
イヤフォンタイプなら少し干渉しますが大丈夫です。

眼鏡だと画面の端がボヤけやすい

コンタクトレンズにしましょう。

重い。おでこに荷重がかかり真っ赤になる

これが今回のブログの本題です。
デメリットを解消すると書いておきながら、ここまで適当な解決策でお茶を濁してしまってすみません。
ここから本気だします。

おでこの痛みはHMZ-T1をつける上で一番大きな問題でもあります。
荷重がかかるなら荷重を減らすのが一番簡単な解決方法です。

という訳で吊るしてみました


ディスプレイの裏に吊るすための支柱をセットし、そこからHMZ-T1を吊るしました。
荷重を減らすために支柱に滑車を取り付け、HMZ-T1と重りを紐で結んであります。

作り方

みなさんもオフィスでHMDを吊るせるように作り方を公開します。
とても簡単ですので是非とも試してみてください!

準備するもの

HMZ-T1

まずは本体であるHMZ-T1ですね。
年末年始はプレミアがついていたりと入手難でしたが、そろそろ手に入るのではないでしょうか。

アーム

吊り下げるためのアームです。
今回はサンコ- クランプ式卓上ブックホルダーを使用しました。
HMDを吊るせるほど剛性が高くないため、HMDを吊り下げるとしなりますが多分大丈夫です。

ロープ

このロープでHMDと重りを結びつけます。

滑車

ロープをなめらかに動作させるための滑車です。

重り

HMDと釣り合いをとるための重りです。
釣り道具店で購入しました。

重りを入れるための袋です。

組み立て

組み立て自体は非常に簡単です。

  1. 机にアームを立てる
  2. アームに滑車を取り付ける

  3. ロープを通す
  4. HMDに結びつける
    おでこパッドの部分が紐を結びつけるのに具合のいい形をしています。
    またちょうど前後のバランスが取れるので取り付け場所として最適ではないでしょうか。
    Sonyのエンジニアが「こんなこともあろうかと」と紐を取り付ける場所としてこっそり用意していたのかもしれません。

  5. 袋に結びつける
  6. 袋に重りをいれてバランスを取る

HMDを軽く指で支えていないと下がってくる様に調整してみてください。

まとめ

使用中の様子です。

この仕組みにより、おでこの赤みが90%減(当社比)と大幅な改善が見られました。
HMDへのケーブルもアーム経由にすることによりケーブルの煩わしさも解消されました。

最初は某巨大掲示板に書かれていたスプリングバランサーを使用して吊るしていました。
どこで手を離してもピタっと止まってはくれるのですが、荷重を減らす目的には向いていませんでした。

改善すべき点

滑車のデキがあまりよくなく摩擦がかなり大きい状態です。
負荷をかけない状態で指で滑車を回すとクルクルとよく回るのですが、ロープを通しHMDと重りの負荷がかかった状態だとまったく回らなくなります。
ベアリング付きのものがあればよいのですが、このぐらいのサイズでは見つけられませんでした。
いい滑車・部材をご存知の方は教えていただけると助かります。

聖闘士星矢Ωが、思ったより面白くて小宇宙が軽く爆発しそうなk_kinukawaです。
今回は、iOSアプリでHTTP通信を行うときの話です。

2012年4月27日 「メインスレッド上で処理している」について一部修正

従来のNSURLConnectionは、レスポンスをdelegateでハンドリングしていました。
そのため、リクエストを投げる箇所とレスポンスを受ける箇所がコード上で離れてしまい、可読性がよくありませんでした。
また、レスポンスを受け取ったあとの処理についても、delegate内で条件分けをして処理をしているうちに分岐/ネスト地獄になりがちでした。

一方、iOS5からNSURLConnectionにsendAsynchronousRequest:queue:completionHandler:というメソッドが誕生しました。
引数を見る限り、GCDを使って非同期リクエストをする系のメソッドのように見えます。
こいつは便利そうですね!!
しかし、利用可能なのはiOS5以降。iOS4をサポートしている限りこれをそのまま使うことは出来ません><
少し前なら「ネットワーク周りめんどくさい!ASIHTTPRequest使えばいいじゃん!」という流れだったのですが、残念ながらサポート終了されるみたいです
(↑URLがcool)

mixiでは、iOS4でもBlocksを使ってHTTPレスポンスを非同期にハンドリングできるMixiAsyncURLConnectionというクラスを作り、実際に公式クライアントアプリ内で利用しています。
今回は、MixiAsyncURLConnectionの簡易版(でも必要機能はそろっている)をサンプルコードの形にまとめました。

使い方

#import "MixiAsyncURLConnection.h"
 
//1.リクエスト作成
NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://mixi.jp/"]];
 
//2.MixiAsyncURLConnectionオブジェクト作成
MixiAsyncURLConnection * connection = [[[MixiAsyncURLConnection alloc]initWithRequest:req timeoutSec:10.0f completeBlock:^(id connection, NSData *data){
    //4.レスポンスハンドリング
    self.textView.text = [[[NSString alloc]initWithData:data encoding:NSJapaneseEUCStringEncoding]autorelease];
} progressBlock:nil errorBlock:nil] autorelease];
 
//3.リクエスト実行
[connection performRequest];

簡単ですね。
順に説明します。

1.リクエスト作成

普通にNSURLRequestを作成します。

NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:3000/"]];
[req setHTTPMethod:@"POST"];
[req setHTTPBody:[@"data=hogefuga" dataUsingEncoding:NSUTF8StringEncoding]];

のようにすれば、GETだけでなくPOSTリクエストも作成できます。

2.MixiAsyncURLConnectionオブジェクト作成

MixiAsyncURLConnectionクラスのイニシャライザでは、以下のパラメータを指定できます。

  • Request – NSURLRequestオブジェクトです
  • timeoutSec – タイムアウト時間を指定します
  • completeBlock – レスポンス受信完了時に実行されるBlockを指定します
  • progressBlock – 途中経過をハンドリングするBlockを指定します
  • errorBlock – エラー発生時に実行されるBlockを指定します

3.リクエスト実行

生成されたオブジェクトのperformRequestメソッドを実行すると、リクエストが飛びます。

4.レスポンスハンドリング

レスポンス受信完了すると、非同期でBlockが呼び出されます。
このBlockの中で、受信データをハンドリングします。
もしリクエストキャンセルをしたい場合は、MixiAsyncURLConnectionオブジェクトのcancelメソッドを呼びます。
とても簡単ですね。

特徴

いくつか注目ポイントがあります。

タイムアウト

NSURLRequestのrequestWithURL:cachePolicy:timeoutInterval:を使うと、リクエストのタイムアウトを指定できますが、実は落とし穴があります。
POSTリクエストをする時に、NSMutableURLRequestのsetHTTPMethodメソッドを使うと、タイムアウトがなぜか効かなくなってしまいます。
色々調べた結果、このような情報を見つけました。残念ですね。
そこで、MixiAsyncURLConnectionでは自前のタイムアウト処理を入れてあります。
これで、POSTリクエストでも自由にタイムアウト時間を設定できます。

Blocksによる非同期処理

completeBlock、progressBlock、errorBlockによって、様々な状態をハンドリングできます。
progressBlockを使えば、写真アップロードの途中経過をプログレスバーにリアルタイムで表示するような処理が簡単に書けます。
errorBlockではNSErrorオブジェクトが返ってくるので、リクエストごとにエラーハンドリングを記述することができます。
注意点としては、errorBlockにはiPhoneの通信エラーのようなNSURLConnectionのエラーが返ってきます。
HTTPのレスポンスコード400番台や500番台はcompleteBlockで受け取ります。

メインスレッド上で処理している

GCDとかNSOperationを使った並列ネットワーク処理のサンプルは、調べるといくつも発見することができます。
しかし、WWDCの動画等を見ていると、UIの処理はもちろん、ネットワーク処理もメインスレッド上で行うべきだとの意見もあります。
上の表記についてご指摘を頂きました。下に詳細を追記します。

MixiAsyncURLConnectionでは、GCDもNSOperationも利用していません。
もしレスポンスのハンドリングが重い処理になる時には、completeBlockの中でGCDを使ってタスクキューへ処理を投げてしまうと良さ気です。

追記
WWDCの動画では、「ネットワーク処理もメインスレッド上で行うべき」とは言っていませんでした。
該当する動画はこちらです。
iOS developerのアカウントでログインすると視聴できます。
WWDC2010 video
Network Apps for iPhone OS, Part 2

また、ご指摘くださったniwさんに素晴らしい調査レポートブログ記事を紹介していただきました。
とても勉強になります。ありがとうございました。

終わりに

実はMixiAsyncURLConnectionの中身は、たった100行ちょっとのNSURLConnectionクラスの拡張です。
しかし、たったこれだけで非常に使いやすいクラスに化けてくれます。
ひと通り動作確認、メモリリークチェックはしています。
機能も最小限なので、ここから自分好みに拡張していくのも楽しいのではないでしょうか。

こんにちは。よういちろうです。今日はOpenSocialなどmixi Platformの話ではなく、最近開発した「あるWebアプリ」についての話をしてみようと思います。

いつの時代も予約って大変!?

このエントリを読んでいる方々の多くは、何らかのシステム開発に関わっている人が多いのではないかと思います。その規模には大小があり、エンタープライズ向け or コンシューマ向けがあり、最近ではWebアプリ or スマートフォンアプリといった区分けもあるでしょう。こういったシステム開発において、よく使われるテスト手法として「ステージングサーバの利用」があげられます。「本番サーバじゃないんだけど、開発機でもない中途半端なもので最終確認する」ためのサーバ、というものですが、一般的には限りなく本番環境に近い環境を準備して、環境の違いからくる不具合などを事前に解消、確認した上で本番環境にリリースする、という目的で実施されます。一発勝負!ダメだったらすぐに引っ込めればいいじゃん!という男らしい姿勢な開発ではなくちゃんと段階踏みましょうね、ということで弊社では現在ステージングサーバでの動作確認をリリース前に実施しています。

このステージングサーバという代物、奪い合いになったりしませんでしょうか?

クラウドや仮想化という分野の進化で、かなりサーバ自体は柔軟に増減をさせられるようになってきましたが、まだまだ多くの企業では限られたステージングサーバの台数内でうまく予定をつけて捌いている、という状況だと思います。弊社もそのうちの一つであり、「ステージングサーバの確保」はエンジニアにとってリリース日を予定通り迎えるための重要な儀式だったりします。サーバの用途によってステージングサーバの台数にばらつきがあって、比較的多く準備されているカテゴリであればいいのですが、少ない台数のカテゴリにおいては「うわ、その期間サーバ空いてないじゃん」とか起きうるわけです。そうなってしまうと「ごめんなさい、動作確認できないっす!リリース日を延期したいっす!」なんてことになってしまいます。決して良い状況とは言えません。

そして、サーバ台数が多いカテゴリになると、別の問題が生じます。どこの会社でも「○○月○○日の○○時から会議室空いてる?」「わかんねーっす!えっと、スケジューラの空き室検索を呼び出して・・・」というように、施設予約の容易さは日々の精神安定上大きな要因だったりしますよね。まだスケジューラというシステムで探せるだけマシかもしれません。これがExcel管理だったりすると・・・、いろんな人が発狂することでしょう。まだExcelならいいかもしれません。これがWikiの表で管理だったとすると・・・、考えたくもないですよね。

弊社は・・・残念!

ではmixiの開発においてステージングサーバの予約はどうだったのか・・・そう、Wikiだったんです。TracのWikiページで表をみんなでいじって、自分で空いてるサーバを目視で見つけて、行を追加していました。百人以上のエンジニアがそれをみんなやっていたんです。もちろん、予約が重複してることも多々あったし、間違えて他人の予約を消してしまうことも当たり前にありました。事故って当然の状況です。

だがしかし、人間ってそんな環境にも慣れてしまうんです。怖いです。入社してすぐに「ありえねー」と言ってた人も、数週間後には何も言わずにWikiを編集しています。大事なことなのでもう一度言います。怖いです。

そんなこんなで生み出しました

便利なアプリを作ってみんなで共有すれば幸せになれる、誰しもがそう考え、でも誰も手がけてきませんでした。そして、そんな状況下で良いアプリを作れば、その人は神になれるかもしれません。そこで、ちょうど作りたいものがなくてアイディアに飢えていたこともあり、自分で作ってみました。

機能的な特徴としては以下があげられると思ってます。

  • 予約したい期間を指定するだけで、空いているサーバを自動的に探して確保してくれる。
  • ダブルブッキングの心配なし。
  • QRコードの表示によって、ガラケーやスマフォでの動作確認をサクッと始められる。

このステージング予約アプリ、最初のバージョンはErlangで書き始めました。具体的には、以下の構成です。

  • Dojo toolkit 1.6
  • Erlang R15B
  • Mnesia (OTPに付属されてるデータベース)
  • mochiweb (Webアプリフレームワーク)
  • meck (モックライブラリ)
  • qrcode (QRコード生成ライブラリ)

せっかくなので、Githubにアップしました。

https://github.com/yoichiro/mixi-staging

ちょっと解説

Dojo toolkitは昔から使っている慣れ親しんだJavaScriptライブラリです。豊富なGUIコンポーネントが揃っていて、特にデザインを頑張らなくてもこういった用途のGUIがそれっぽく作れます。

Erlangには、処理系に最初からMnesiaデータベースが含まれていて、情報の永続化をすぐに始めることができるようになっています。これには、Erlangのレコード単位で情報を格納していくことができます。例えば、

-record(user, {
    id :: string(),
    name :: string(),
    email :: string(),
    password :: string(),
    irc :: string()}).

というuserレコードが定義されていたとすると、

NewUser = #user{
    id = '12345',
    name = 'Yoichiro Tanaka',
    email = 'a@b.com',
    password = 'hogehoge',
    irc = 'yoichiro'},
F = fun() ->
        mnesia:write(NewUser)
    end,
case mnesia:transaction(F) of
    {atomic, ok} ->
        {ok, NewUser};
    Other ->
        {error, Other}
end.

という感じで1レコードを格納でき、

Q = qlc:q([X || X <- mnesia:table(user),
                X#user.id = '12345']),
F = fun() ->
        qlc:e(Q)
    end,
{atomic, [User12345 | _]} = mnesia:transaction(F).

という感じでリスト内包表記を使って1件取得を行うことができます。なんて美しさ。。。

Mnesiaデータベースは標準搭載なものなので「機能もそれなりなんでしょ?」と思うかもしれませんが、実際にはかなり高機能です。レプリケーション構成をしたければ、各ノードでMnesiaを起動しておいて「となりにMnesiaデータベースがいるよ」と関数を一つ叩けば、それだけでレプリケーションが開始されます。この設定はデータベース単位ではなく、レコード単位で可能です。さらに、特定のレコードについて、シャーディングも簡単です。「シャーディングして」と指示を関数呼び出しするだけで、複数ノードに分けて分散格納してくれます。取り出す際にそれを意識する必要はありません。

Mnesiaを使ったデータベース関連の処理は、db.erlにまとめてあります。行数がかなりいってますが、後半は単体テストのコードです。Mnesia関連の関数をmeckを使ってモックに差し替えて、各関数の動作を確認しています。

https://github.com/yoichiro/mixi-staging/blob/master/src/db.erl

OTPに標準で含まれているライブラリは非常に充実しているのですが、手軽にWebアプリケーションを書くために、いつもmochiwebを使っています。これはHTTPサーバの機能を有していて、各リクエストに対する処理を自分で追記していくことができます。例えば、

loop(Req, DocRoot) ->
    "/" ++ Path = Req:get(path),
    try
        case Req:get(method) of
            Method when Method =:= 'GET'; Method =:= 'HEAD' ->
                handle_get(Req, Path, DocRoot);
            'POST' ->
                handle_post(Req, Path, DocRoot);
            _ ->
                Req:respond({501, [], []})
        end
    catch
        ...
    end,
    ok.

という感じでHTTPメソッドにより処理分岐し、handle_post()関数の中で、

process_flag(trap_exit, true),
Params = Req:parse_post(),
"ajax/" ++ Command = Path,
Pid = spawn_link(list_to_atom(Command), execute, [self(), Params]),
receive
    {Pid, Status, Response} ->
        Encoder = mochijson2:encoder([{utf8, true}]),
        Req:respond({Status, [{"Content-Type", "application/json"}],
                     Encoder(Response)});
    Other ->
        io:format("Error in route_process: ~p~n", [Other]),
        ...
end.

という感じで、各リクエストに応じた子プロセスを作ってメッセージを投げて処理を委譲し、受信した返信内容をJSONにエンコードしてクライアントに返却する、ということをmochiwebで実現できます。上記では自身のErlangノード内にプロセスを生成していますが、管理下にある別マシンの別ノードを指定すれば、異なるサーバの資源を使って処理を行うことができます。

具体的な処理は、各モジュールに分けて記述しています。例えば認証処理であればauthenticate.erlに、ステージングサーバ予約処理であればreserve.erlに、それぞれ書いています。ただし、具体的な予約処理はデータベースに近いところ、つまりdb.erlに書いています。これは単体テストのしやすさを優先したためです。HTTPリクエストを受け取って結果を返す各モジュールの単体テストは、実際にプロセスを作ってメッセージを投げ、返送されてくるメッセージの内容が妥当かどうかを検証するようにしています。

https://github.com/yoichiro/mixi-staging/blob/master/src/authenticate.erl
https://github.com/yoichiro/mixi-staging/blob/master/src/reserve.erl
https://github.com/yoichiro/mixi-staging/blob/master/src/db.erl#L291

Mnesiaデータベースのレプリケーションと、複数ノード下でのプロセス分散生成、フロントにApache+mod_balancerを置いて分散、などしていけば、かなりスケールしやすい構成を低コストで得ることが可能です。イメージとしては以下の図のようになるでしょう。Erlangが提供してくれる透過的なメッセージパッシングモデルによって、プログラムはシンプルなままで副作用を意識することなくスケール可能な仕組みを作ることができます。

将来エンジニアが数万人になり、ステージングサーバの台数が4桁いったとしても、まったく問題なさそうですね!

現在は?

このステージングサーバ予約アプリですが、先週の16日から実際に社内で運用を開始しています。上述した最初のバージョンに比べて、現在は以下のような機能追加や変更をかけています。

  • 予約の用途を並べて、今どんな動作確認が社内で行われているかを一覧で俯瞰できるようにした
  • 1ヶ月のサーバ予約状況を見れるようにした
  • 自分が予約しているサーバの一覧を見れるようにした
  • 最近追加されたサーバの一覧を見れるようにした
  • 全体的な利用統計を表示するようにした
  • 好きな画像を背景に表示できるようにした
  • 諸事情により、ErlangではなくRuby on Rails+MySQLで書き換えた

すでにちょっとした機能追加を加えてくれる人も登場してきました。自分たちの道具は自分たちで作る、こういったことをサクッとできる環境、そして自分の実力をいつまでも保っていきたいと考えている37歳の春でした。

どうも、佐野です。今回は UIAlertView の拡張カテゴリを作って、delegate ではなく Block でコールバック処理を記述するための拡張カテゴリの作り方を紹介します。

iOS4 から Objective-C では Blocks という独自のクロージャ機能が搭載されました。これによってアニメーションの記述やコールバック処理などグッと直観的・効率的に記述できるようになったのですが、残念ながら UIKit によって提供されているクラスの多くはまだ Blocks に最適化された作りになっていません。UIAlertView もそのひとつです。

UIAlertView は、その delegate を実装することでユーザアクションに対する処理を記述する訳ですが、どうも使い勝手が悪い。例えば、同じで画面内で2通りの UIAlertView を表示するような UIViewController を作る場合、その実装はこんな風になるでしょう:

@implementation MyViewController
 
...
 
// 1個目のアラートの表示処理
- (IBAction)action1:(id)sender {
    // アラート生成
    UIAlertView *a1 = [[[UIAlertView alloc] initWithTitle:@"Alert1"
                                                  message:@"..."
                                                 delegate:self
                                        cancelButtonTitle:@"キャンセル"
                                        otherButtonTitles:@"OK", nil] autorelease];
 
    // 識別のためのタグを設定
    a1.tag = 1; 
 
    // 表示
    [a1 show];
}
 
// 2個目のアラートの表示処理
- (IBAction)action2:(id)sender {
    // アラート生成
    UIAlertView *a2 = [[[UIAlertView alloc] initWithTitle:@"Alert2"
                                                  message:@"..."
                                                 delegate:self
                                        cancelButtonTitle:@"キャンセル"
                                        otherButtonTitles:@"OK", nil] autorelease];
 
    // 識別のためのタグを設定
    a2.tag = 2;
 
    // 表示
    [a2 show];
}
 
...
 
// アラートのdelegateメソッド
// alertViewのtag属性によってアラート1,2を識別し、処理を分岐する
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    // 1個目のアラートに対する処理
    if(alertView.tag == 1) {
        if(buttonIndex == 0) {
            ... // [キャンセル]処理
        } else {
            ... // [OK]処理
        }
    }
    // 2個目のアラートに対する処理
    else if(alertView.tag == 2) {
        ....
    }
}

別々の UIAlertView をわざわざ共通の delegate メソッドに通してその中で再び分岐させているのが嫌です。これが500行ぐらいのコードになると処理の流れが分散してとても読みづらいものになります。ここはやっぱり Blocks を使って、こんな風にカッコ良く書きたいモノです:

    // コールバックブロックを生成
    void (^alertCallback1)(NSUInteger) = ^(NSUInteger buttonIndex) {
        if(buttonIndex == 0) {
            ... // [キャンセル]処理
        } else {
            ... // [OK]処理
        }
    };
 
    // コールバックを渡してアラートを生成
    UIAlertView *a1 = [[[UIAlertView alloc]
                        initWithTitle:@"Alert1"
                        message:@"..." 
                        callback: alertCallback1 // ← コールバックブロックを引数に!
                        cancelButtonTitle:@"キャンセル"
                        otherButtonTitles:@"OK", nil] autorelease];
 
    // 表示
    [a1 show];

alertCallback1delegate メソッドに代わるブロックになっています。あるいは何の変数代入もせずこんな風に書くのも良いでしょう:

    // アラート表示
    [[[[UIAlertView alloc] 
       initWithTitle:@"Alert1"
       message:@"..."
       callback:^(NSInteger buttonIndex) {
           if(buttonIndex == 0) {
               ... // [キャンセル]処理
           } else {
               ... // [OK]処理
           }
       }
       cancelButtonTitle:@"キャンセル"
       otherButtonTitles:@"OK", nil]
      autorelease]
     show];

アラートの生成とその後の処理を同じところにまとめて書けるのがいいですし、UIViewControllerUIAlertViewDelegateプロトコルを実装する必要もなくなりとてもスッキリします。 という訳で早速この UIAlertView 拡張カテゴリを作りましょう。まずはヘッダ:

UIAlertView+BlocksExtension.h
@interface UIAlertView(BlocksExtension)
 
typedef void (^UIAlertViewCallback_t)(NSInteger buttonIndex);
 
- (id)initWithTitle:(NSString *)title
        message:(NSString *)message
        callback:(UIAlertViewCallback_t)callback
        cancelButtonTitle:(NSString *)cancelButtonTitle
        otherButtonTitles:(NSString *)otherButtonTitles, ...;
 
@end

(void (^)(NSInteger buttonIndex)) というブロック型を UIAlertViewCallback_ttypedef しました。これがクリックされたボタンのインデックスを引数に取るコールバックブロックの型になります。

さて、実装はどうしましょう。このコールバックを呼ぶための delegate インスタンスを作って UIAlertView に持たせたい訳ですが、そのメモリ管理をどう行うかが問題になります。UIAlertViewdelegate プロパティは assign 属性なので、インスタンスによって保持・解放されません。んーむ、どうしよう。

解決!メモリ管理は誰かにやってもらうという発想を捨て、ここでは delegate インスタンスが、役割を終えたら自ら消滅してくれるようにします。そのクラスを UIAlertViewCallback として、まずヘッダはこんな感じ:

UIAlertViewCallback.h
@interface UIAlertViewCallback : NSObject <UIAlertViewDelegate> {
    UIAlertViewCallback_t callback;
}
 
@property (nonatomic, copy) UIAlertViewCallback_t callback;
 
- (id)initWithCallback:(UIAlertViewCallback_t) callback;
 
@end

UIAlertViewDelegate プロトコルの実装を宣言している点に注意してください。実装はこうです:

UIAlertViewCallback.m
@implementation UIAlertViewCallback
 
@synthesize callback;
 
- (id)initWithCallback:(UIAlertViewCallback_t)aCallback {
    if(self = [super init]) {
        // コールバックブロックをセット
        self.callback = aCallback;
 
        // 自分自身を保持!
        [self retain];
    }
    return self;
}
 
// UIAlertView の delegate メソッド
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    // コールバックを呼ぶ
    if(callback)
        callback(buttonIndex); 
 
    // コールバックを呼び終えたら自分自身を解放する!
    [self release];
}
 
- (void)dealloc {
    self.callback = nil;
    [super dealloc];
}
 
@end

まずコンストラクタの中で自分自身を保持しています。これによって他のインスタンスからの参照がなくても dealloc されることはありません。次に UIAlertViewDelegate- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex を実装しており、その中でコールバックブロックを呼んで、それが終わったら自分自身を解放しています。こうすることで、インスタンスはちゃんと自分の役割を終えたら消滅してくれる訳です!

これでようやく準備完了、あとはこれを使って UIAlertView の拡張カテゴリを実装しましょう:

UIAlertView+BlocksExtension.m
@implementation UIAlertView(BlocksExtension)
 
- (id)initWithTitle:(NSString *)title message:(NSString *)message callback:(UIAlertViewCallback_t)callback  cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... {
 
    self = [self initWithTitle:title message:message delegate:nil cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil];
    if(self) {
        // otherButtonTitles, ... を手動でセット
        va_list args;
        va_start(args, otherButtonTitles);
        for (NSString *arg = otherButtonTitles; arg != nil; arg = va_arg(args, NSString*)) {
            [self addButtonWithTitle:arg];
        }
        va_end(args);
 
        // delegateにUIAlertViewCallbackをセット
        self.delegate = [[[UIAlertViewCallback alloc] initWithCallback:callback] autorelease];
    }
    return self;
}
 
@end

va_list なんかを使っているのは、可変長引数列をそのまま他のメソッドコールの引数として渡すことができないためです。今回のポイントはその後の self.delegate = [[[UIAlertViewCallback alloc] initWithCallback:callback] autorelease]; と、先ほどのコールバック用のインスタンスを生成してセットしているところです。

どうですか、なかなかクールでしょう。このやり方は UIActionSheet にもそのまま適用できますし、あとは NSURLConnection をラッピングして通信コールバックをブロックで記述するようなクラスもこのやり方で作れます。条件は Delegator (UIAlertView, UIActionSheet, NSURLConnection など) が消滅するタイミングで、1度だけ必ずコールされる delegate メソッドがあることです。だからこそそこで確実に delegate インスタンスを消滅させることができる訳です。

やり方としてはかなりトリッキーですし、 UIKit がちゃんと Blocks 対応を進めればこの拡張カテゴリは不要になりますが、こうやって開発者が一歩先に進んで低いレイヤーから独自にフレームワークを拡張させることができるのは Objective-C プログラミングの面白いところだと思います。

ではまた次回、お会いしましょう。

こんにちは、UX統括部の横幕です。すっかり春になって、桜を眺めるのが気持ち良いですね。

最近、社内で活発に「デイリースクラム」が行われるようになりました。
日々、チームメンバーの持っているタスクの進捗を確認し合うことで、スケジュール感の共有・調整、あるいは、チームメンバー同士でタスクの振り分けを見なおしたりなどができ、チームの有機的な動きを作ることが出来るようになってきています。

さて、そんななかで、今回は、プロジェクトを進める上で、また日々のデイリースクラムをする上で重要な「タスクの見積り」についてお話しようと思います。

1. 見積る前に

1-1. 計画を立てよう

タスクの見積りをする前に、何をするのか、その計画を立てていきます。

・フィーチャーを考える

フィーチャーとは、お客様に提供する機能・価値のことです。アジャイルなプラクティスでは、ユーザーストーリーと呼んだりもするようです。

  • 新機能Aを作る
  • 機能Bに追加機能B-2を盛り込む
  • 機能Cを××に最適化する

ここで大事な事は、お客様にどんな価値が提供できるかどうか、ということです。つまり、「サーバを増強する」や「DBのスキーマを変更する」といったものはフィーチャーではない、ということです。
加えて、各フィーチャーは独立している方が良いとされます。なぜなら、予測不能な割り込み等でスケジュールがピンチになっても、フィーチャーが独立していれば、優先度がつけやすくなり、死守すべきラインを明確にしやすくなるからです。

このようにすることで、お客様からは、どれくらいの価値あるものが提供されるかが分かるようになります。
また、開発チームにとっては、開発・実装のゴールが見える化されます。
そして、ビジネスの観点からは、各フィーチャーの優先度が分かりやすくなるので、戦略が立てやすくなります。

・より詳細に

フィーチャーを決めたら、そのフィーチャーの中で「どんなことが出来るようになるのか」を決めます。
フィーチャーだけでは、実際何を実現すれば良いのかが明確になっていないため、噛み砕いて「どんなことが出来るようになるのか」を考える必要があります。
アジャイルなプラクティスでは、プロダクトバックログや、ストーリーなどと呼ぶようです。

  1. 新機能Aを作る
    • ○○を出来るようにする
    • ××を出来るようにする
  2. 機能Bに追加機能B-2を盛り込む
    • 新たに、△△が出来るようになる
  3. 機能Cを××に最適化する
    • ××で正常に動かなかった機能Cが動くようになる

このようにすることで、お客様からは、実際に何が実現されたかを知ることができます。
また、開発チームは、具体的な動作・実装の目標が立てられます。

・タスクを作る

「どんなことが出来るようになるのか」が決まったら、細かいタスクを洗い出していきます。アジャイルなプラクティスでは、シナリオとも呼ばれるようです。

  1. 新機能Aを作る
    • ○○を出来るようにする
      • DBのスキーマを決める
      • 画面を作る
    • ××を出来るようにする
      • DBのスキーマを決める
      • プロシージャを作る
  2. 機能Bに追加機能B-2を盛り込む
    • 新たに、△△が出来るようになる
      • DBのスキーマを変更する
      • 機能Bの画面に、追加機能B-2の導線を置く
  3. 機能Cを××に最適化する
    • ××で正常に動かなかった機能Cが動くようになる
      • 正常に動かない理由を調べる

ここで大事な事は、開発チームメンバー全員が、「どんな実装になるのか」の認識が合うようにタスクを分けるようにすることです。
Aさんはストーリーの段階で理解できたとしても、Bさんが分からなければ、「正確な見積り」ができなくなってしまいます。
なので、できるだけチームメンバー全員が納得できる粒度まで落とし込みます。

1-2. 計画を立てる時のポイント

・みんなが分かりやすい言葉にする

フィーチャー・ストーリー・タスク(シナリオ)のどの粒度でも、分かりやすく書くことは重要なポイントです。
分かりやすく書くことで、プロジェクトが、何のために、何をしているのかが見える化されるようになり、
そのために必要な調整も(プロジェクトに直接関係する人以外にも理解できることで)よりスムーズに出来るようになります。

・独立したものにする

各フィーチャーが密接に関連しあっていると、そのための調整コストが発生してしまいます。
また、スケジュールがピンチになった時に、フィーチャーやストーリーなどが密接に関連しあっていると、死守すべきラインが広くなって開発者がつらい思いをしてしまいます。
なので、出来る限りフィーチャーやストーリーなどは独立している方が良いです。

2. 見積りを立てる

2-1. 見積りとは

タスクの見積りとは、そのタスクをやり遂げるのにどのくらいのコストがかかるかを数値化することです。

しかし、コストと一口に言っても、かかる時間のことや、かかるお金のこと、人数など様々な要素が含まれています。これら1つ1つを精密に計算しても良いかも知れませんが、
往々にしてそれら全部を計算し尽くす時間は無いものです。

2-2. 単位に重要な意味を与えない

そこで、工数の単位を「ポイント」で出します。
ポイントという単位に特に意味は無く、「このタスクの工数が5ポイントだから、それと比較すると、このタスクは…」と言ったような、相対的な比較に使うためのもの、ということにします。

このようにすることで、お客様やビジネスの観点からは、詳細は分からなくとも、提供されるフィーチャーがどのくらいの重要性を持っているかが分かるようになります。
また、開発チームからは、一日にどのくらいのポイントを消化していけば納品可能かを測る指標を持つことが出来るようになりますし、相対的に見積もるので、各タスクの難易度も見える化され、何処に重点的に力を注げばよいかが分かるようになります。

2-3. 見積もってみる

実際の見積りは、開発チームのメンバー全員で行います。

一人で見積ると、自分が理解していることは低めに見積もられ、理解していないことはかなり高めに見積もられてしまい、実際にかかる工数と乖離したものになってしまいかねません。
また、実は○○というハマりどころがあって、という発見があったり、思ったほど重たくない(軽くない)ことが見積りの段階で判明した、などという点でも、チームメンバー全員で見積ることは重要です。

2-4. 楽しく見積る

さて、あとはタスクに見積りの数字を当てはめていくことになるのですが、ただ単に数字を言い合うだけの黙々とした作業ではつまらないですよね。
そこで弊社のAndroid(TM)開発チームでは、「PLANNING POKER」を利用して見積りを行なっています。

PLANNING POKERとは、「?, 0, 1/2, 1, 2, 3, 5, 8, 13, 20, 40, 100, 無限大」が書かれたカードのセットを1人1つずつ手札にして、
あるタスクに対する工数はどのくらいかを、そのカードの中から選び、全員一斉に出しあって工数感の認識合わせをするためのツールです。

以下のようなルールに基づいて工数を見積もっていきます。

  1. あるタスクの工数のポイント数を、チームメンバーそれぞれがカードの中から選びます(まだ他のメンバーには見せない)
  2. 全員が選び終えたら、一斉にカードを出し合います
  3. 全員の出した数字が一致していたら、出した数字を、そのタスクの工数とします
  4. 全員の出した数字が一致しなかった場合、なぜその数字にしたかの理由をそれぞれ述べていきます
  5. その理由を元に、もう一度カードを選び直します
  6. 以下、全員の数字が一致するまで1~4を繰り返します

最初に一致しない場合でも、おおよそ3回くらい繰り返すと、だいたい数字が一致するようになります。
もしそれ以上繰り返しても一致しないときは、タスクの洗い出しを見直すか、あるいは数字が近いのであれば、全員の出した数字の平均値をタスクの工数とする、という選択も可能です。

ただ、1つのタスクとして、40や100といった数字がつくようなものは、改めて「具体的に何を実現するのか」「何をすればいいのか」を掘り下げる必要があるものです。

3. 正しく見積るには

例えば、チームに初めて配属された人がタスクを見積るのは大変です。経験のない人が見積ることはなおさら大変です。
このような状況において、正しく見積もりを行うためにはどのようにしたらよいでしょうか(正しく見積もれなかったからと言って責められるようなものではありません)。

3-1. ストーリーやタスクの詳細をみんなで考える

ストーリーやタスクに書かれている言葉が、初めての人にとって抽象的すぎる場合には、より具体的に表現していくことが大切です。
みんなが分かりやすい言葉にする、というのは、簡潔にかければそれで良い、ということだけではなく、時にはより具体的に表現することも検討してみる、という柔軟性が求められています。

3-2. 分かる人に聞く

たとえば、私はまだコードレビューをするという経験がないので、コードレビューにどれだけの工数がかかるかわかりません。
チームの中で分かる人がいれば、その人の話を聞いてみてから決める、ということも、正しく見積るために重要なことです。

3-3. とりあえずやってみる

やってみて初めて、「このくらい時間・労力がかかる仕事だったのか」ということが体験できます。
体験があれば、工数の見積りもしやすくなります。

3-4. 基準を決める

あるタスクの工数を見積るとき、全員が大体同じ認識を持っていそうなタスクの工数を基準に相対的に見積ると、見積りの数字を決める理由の一助となります。
「このタスクよりは考えることが多そうだ」、「このタスクに比べると、影響範囲は小さそう」などのような話し合いがしやすくなるので、
PLANNING POKERの繰り返しも少なくなります。

3-5. とりあえず言ってみる

よく分からなければ当てずっぽうで言ってみるのが良いです。
みんなの認識と違っていれば、それぞれが理由を述べ合うので、その理由を元にもう一度考えなおすことが出来るのがPLANNING POKERを使って見積ることの良いところです。

4. まとめ

いかがでしたでしょうか。
計画づくりにしても、見積りにしても、一番大事な事は、みんなが分かりやすいこと、みんなの認識が揃っていることです。
こと見積りについては、PLANNING POKERが、楽しく、かつ正しく見積る手助けをしてくれます。
是非実践してみてください!