HMDT | HMDT Blog | ページ 5

カテゴリー : HMDT

Xcode使いがEclipseにぶちまける10の不満


最近Android開発をやらされてます。「やらされている」というのは、特定の案件のためというより、時流としてもういい加減無視できないので会社の方針としてやることにした、でも非常に嫌々な気分満載、というニュアンスを含んでいます。

私も大人になりましたし、仕事ですから、Android端末を手にとっても、それを7階のオフィスから投げ捨てるようなことはなくなりました。ニッコリ笑って操作しますが、左上に戻るボタンが見つからない瞬間に、床に叩き付けてしまいます。ハードウェアボタンかよ、くそっ。あーもう正直にいいますが、嫌いっすよAndroid。iPhoneラブですよ。

当方生粋のMacユーザなので、ものの見方にバイアスがかかっていることは自覚しています。いまのAppleからは想像もできませんが、Macを昔から使っている人は虐げられていた期間が長いので、ひねくれてねじまがっているんですよ。その世界観では、悪の帝国Googleに立ち向かうジェダイの騎士Appleです。iPhoneを携えてAndroidを駆逐しなくては!という義憤にかられます。ですが、最近のAppleのビッグプレイヤーぶりを見ると、隔世の感というか、自分がジジイの繰り言になっているのがよく分かります。Appleはもう俺たちなんか必要としていないんだ。しゃーねーなー、Android開発もやるか。ちなみに、Microsoftはすでに滅んだ過去の帝国です。

で、Android開発をやると、最初に立ちはだかる壁がEclipseですよ。開発環境。当方OS XおよびiOS開発を生業としていますので、開発環境はXcodeこれ一本。前身であるProject Builderから使い続けていますよ。毎日12時間は使い続けているXcodeは、もう自分にとって体の一部です。

そんな私がEclipseを使いました。もうね、言っちゃいますけどね、ホントの気持ち言っちゃいますけどね、最悪ですよこれ。起動からクラッシュするまで、最悪の気分ですよ。まずアイコン、ださっ。起動、遅っ。画面表示、無駄っ。ツールバー、汚っ。エディタ、重っ。プロジェクト管理、訳分からん。ガベージコレクションの最中にクラッシュ、バカじゃねーの。

いや、知ってます。知っているんです。Eclipseを使い続けてきた人がXcodeを使ったら、これとまったく同じ感想を抱くことを。EmacsやVisual Studioを使っている人がXcodeに抱く感想も、同じことを。みんな、自分が慣れ親しんでいる環境が一番なんです。「使いやすさ」に対する絶対的な尺度はないんです。「慣れ」が最も大事なんです。開発者が、他の環境にあわせるのは、並大抵のことではないんです。

あれですね。開発環境を移すということは、ある意味海外に移住することに似ていますね。言葉が違う。習慣が違う。文化が違う。何をやるにもストレスフルです。でもそれは、別の視点を獲得することでもあります。違いを認め、新たな考え方を習得することで、より豊かな生活を送れるようになります。

よーし。Eclipseを使うという事は、異文化への挑戦である。ここはひとつ、腹を据えて触ってやろうじゃないか!………(触っている)………(触っている)………(触っている)。分からーーーん!ぜんぜん思い通りに動かないーーー!海外で迷子になったけど、看板も読めないし、誰にも話しかけられない気分だーーー!

なぜこんなにイライラするのか?それは「この操作はXcodeだったらこうするのに、Eclipseだとやり方が分かんない、または結果が期待と違う」からです。だから、やり方が分かればいいんだけど、マニュアルもドキュメントも量だけ膨大で、そのくせ大したこと書いていなくて、もう何をどう探せばいいのか分からんちんです。

そこで!Eclipseの操作で何が分からないのか、または気に食わないのか、まとめてみました。Xcodeをずっと使っていた人が、Eclipseのここでつまずいているよ、というのをまとめてみました。でもこれって、とても基本的な操作ばっかりなので、たぶんEclipseでもできると思います。ってか、できてくれよ。なので、これを読んで解決方法が分かる人がいたら、「こうすればいいんだよ、愚か者め」と教えてください。

1. メソッドの一覧表示および絞り込み検索

エディタでソースコードを編集するとき、そのクラスに含まれるメソッドの一覧を素早く表示、かつ絞り込み検索もしたい。

Xcode

エディタの上部にあるポップアップメニューをクリックすることで表示できます。

xcode_popup1

表示順は、ソースコードに書かれた順です。ポップアップを表示した状態でキーボードから文字を入力すると、絞り込み検索ができます。

xcode_popup2

絞り込みは、前方一致ではなく部分一致です。だから、メソッドの名前の一部を覚えていればオッケー。つまり「あのメソッドの編集したいな、えーっと名前なんだっけ、viewが含まれていたっけ」といううろ覚えな感じでも、素早く検索および移動ができます。

また、#pragma markを使う事で、メニューの中に区切り線や特別な文字を追加できます。上の図における”View Life cycle”とかですね。これにより、自前でメソッドをカテゴリに分けて管理できます。

Eclipse

Eclipseでのメソッド一覧表示は、これかな?Outlineの表示。

eclipse_outline0

一覧表示はありました。メソッドだけじゃなくて、スタティック変数なんかも出てくるところはXcodeより機能は上ですね。絞り込み検索を行うのは、Filtersでしょうか?

eclipse_filters

Fitersの画面で、キャラクターマッチングが行えます。でもこれ、「パターンにマッチした名前を隠す」なのね。なんで?マッチしたものを表示するなら分かるけど、なんで隠すの?どんなシチュエーションでこれ有用なの?結局、絞り込み検索はできないの?

2. 補完入力

クラス名、メソッド名やキーワードを補完入力したい。私は、補完入力肯定派です。

Xcode

あるメソッドの中で、次のコードを入力してみます。

self = [super initWithNibName:nibName bundle:nil];

まず、’s’を入力しまう。すると、’self’を補完するための候補がポップアップで表示されます。selfが出てこないときは’se’まで入力します。

xcode_comp5

そこでselfを選択して、タブキー(またはエンター)をヒット。文字列が補完されるので、続いて’ = [su’までを入力します。すると’super’を補完するための候補が表示されます。

xcode_comp1

タブキーをヒット。文字列が補完されます。続いて、’ i’を入力。iで始まるメソッドの候補が表示さます。

xcode_comp2

‘in’まで入力すると、絞り込まれる。ここでわざと、’initWithCoder:’を選択。タブキーを押します。

間違えたので、’initWith’まで削除してエスケープキーを押します。または、’initWit’まで削除して’h’を入力します。すると再び、候補が表示されます。

xcode_comp3

今度はinitWithNibName:bundle:を選択。最後に引数を入力して完了。このときローカル変数であるnibNameや、defineであるnilも補完されます。

まとめると、selfやsuperといったキーワードが補完されます。メソッド名が補完されます。一度削除して、再度入力するときも補完されます。あと、インスタンス変数やローカル変数も補完されます。

Eclipse

あるメソッドの中で、次のコードを入力してみます。

super.onCreate(savedInstanceState);

まず、’s’を入力します。何も補完されません。なんで?キーワードは補完されないの?どっかで設定する必要があるの?しょうがないので、’super’まで入力します。そして’.’を入力します。メソッド名を補完するための候補がポップアップで表示されます。

eclipse_comp

ここで’onCreate()を選択してエンターを押します。すると、’onCreate(savedInstanceState);’まで入力されます。これで終わり。ふむ、素晴らしい。

でも、ちょっと変更したいと思って、メソッド名を’onCrea’まで削除します。このとき、どうやっても補完ポップアップが表示されません。なんで?エスケープキーを押すとかして、この状態から候補を表示できないの?しょうがないからメソッド名を全部削除して、再び’.’を入力すると候補が表示されます。つまり、一度間違えると全部消さないといけないのか。

また、引数の’savedInstanceState’は自動入力されましたが、これも一度削除して、もう一度’s’と入力しても補完候補は表示されません。なんで?ローカル変数は補完の候補にならないの?

まとめると、superというキーワードが補完されません。クラスの後にドットを入力すると補完候補が表示されますが、候補を一度決定すると途中から補完させる事はできません(もう一度ドットの入力からやりなおす)。インスタンス変数やローカル変数は補完されません。

Xcodeは、クラス名、メソッド名、インスタンス変数名は、宣言のときにただ一度書くだけで、あとはフルに入力する事は無いです。だって補完されるから。したがって、Xcodeではシンタックスエラーはありえない。だって補完されるから。一度入力したメソッドを変更したいときも、一部を削除するだけで、またすぐ補完候補が表示されます。

Eclipseも補完はされるんだけど、一度間違えると修正するには全部消してもう一度ドットの入力からやり直さないといけない。これがとってもストレス。

3. APIドキュメント

各種フレームワークのAPIドキュメントを表示したい。そして、クラス名やメソッド名で検索したい。

Xcode

Helpメニューから「Documentation and API Reference」を選択します。専用のツールが表示されます。検索バーにクラス名、メソッド名、変数名などを入力します。インクリメンタルサーチされます。

xcode_apiref

選択すればその項目が表示されます。こんだけ。疑問の余地無く単純です。

Eclipse

Eclipseにはドキュメントをブラウズするためのツールが、、、ない!?ないの?ないこたないと思うんだけど、見つからないよ。

しょうがないので、Webブラウザを使います。Webブラウザを起動して、http://developer.android.com/reference/を開きます。パッケージ一覧が出てきます。右上の検索フィールドから、クラス名が検索できます。インクリメートサーチできます。

eclipse_apiref

クラス名は検索できるんだけど、メソッド名は?メソッド名での検索の仕方が分からない。できないの!?そんなバカな。ないわけないよね?そんなオブジェクト指向開発環境あるはずがない。きっと秘密の方法があるんだよね!?

4. シミュレータ

テストのために、Mac上で動作させたい。シミュレータまたはエミュレータを使いたい。

Xcode

動作するスキームとして、iOSシミュレータを選択します。

xcode_scheme

ビルドして実行します。シミュレータが起動して実行されます。以上、おしまい。

Eclipse

Android Virtual Device Managerから、Android Virtual Deviceを作成します。選択して実行します。エミュレータ起動画面が表示されるので待ちます。

eclipse_emu

待ちます。待ちます。待ちます。5分待っても画面が変わらないので、強制終了します。以上、おしまい。

なんかネットで検索すると、Intel製?のエミュレータがあるらしくそれを使えという事らしいけど、純正でこんなにも使い物にならないってのは、どうよ。ダメすぎじゃん。

iOSはシミュレータ。Androidはエミュレータ。つまり、iOSはOSおよびアプリをx86にネイティブコンパイルしたものを使います。だから速い。AndroidはARMチップをx86上でエミュレートして走らせます。そりゃ遅いに決まってます。純正でまともな速度で動作するエミュレータ(もしくはシミュレータ)作ってよ。

5. Subversionとの統合

開発環境と、各種SCMを連携させたい。うちの会社はSubversion使っているんで、Subversionが動けばいいです。

Xcode

新規プロジェクトを作成して、Subversionに入れる。その状態でプロジェクトを開く。これだけで連携します。リポジトリアドレスの入力などは必要なし。プロジェクトにファイルを追加すると、Subversionにaddされますし、削除するとdeleteされます。編集したら、commitします。

Eclipse

EclipseでのSubversion統合は、できるんだよね、きっと?ちゃんと調べれば、どこかにやり方が書いてあるんだよね?ただXcodeみたいに設定なしでできるというわけではないのかな?

6. エディタの2画面表示

Xcode

Xcodeでは、エディタを2つ並べて連携させる事ができます。このとき、右側のエディタはアシスタントと呼ばれます。

xcode_assistant

アシスタントにはざまざまな表示が可能ですが、カウンターパートと呼ばれるものがあります。これを使うと、左側に.mを表示するとき、右側に自動的に.hを表示させることができます。その逆で、.hを選択したときに.mを表示する事も可能。

また、左側で.xibや.storyboardを表示して、右側に対応するコントローラクラスを表示する事も可能。インタフェースをデザインしながら、対応するソースコードを実装できます。

Eclipse

Javaはヘッダファイルがないから、この機能への要求が低いのは分かります。でも、layoutを編集しながらActivityのコードをいじれたら、便利だと思うんだけどな。

7. インスペクタ

Xcode

Xcodeの、ユーザインタフェース編集のためのインスペクタ画面です。

xcode_inspector

Eclipse

Eclipseの、ユーザインタフェース編集のためのプロパティ画面です。

eclipse_property

なめとんのか。手抜きだ手抜き。

8. 操作をアニメーションで確認する

XcodeもEclipseも、基本的に一画面をタイル分割して各種情報を表示しています。そのタイルを隠したり表示したりできるんだけど、Xcodeはそのときにアニメーションが伴います。Eclipseは伴わない。正直Eclipseだと、何がどこに移動したのか分かりません。操作をした直後は完全に動揺し、そのまま30秒画面を凝視した後で、何がどこに動いたのかが理解できます。これがアニメーションがあれば、その瞬間に分かるものです。

アニメーションはただの派手な演出ではありません。ユーザを導くガイドラインです。Eclipseは、それがまったくないです。

9. スタックトレースを見やすく表示する

Xcode

Xcodeのスタックトレースです。

xcode_stack_trace

Eclipseのスタックトレースです。

eclipse_stack_trace

ぐは。ログかよ!

これは悪意のある比較で、Eclipseにもちゃんとリストで表示するスタックトレースは見つけました。

eclipse_stack_trace1

でも、ログで吐くスタックトレース(しかも改行されてリストで表示)は、衝撃だったな。これしかなかったらどうしよう、と思った。

10. ワークスペースを複数開く

Xcodeでは、開発は「プロジェクト」が基本。プロジェクトを複数まとめた「ワークスペース」も作成できます。ワークスペースは複数作成し、同時に開く事ができます。

Eclipseでは、開発は「ワークスペース」が基本。ワークスペースで複数の「プロジェクト」をまとめます。ワークスペースは複数作成できますが、同時に開くことはできません。なんでじゃ!しかもワークスペースを切り替えるときに、一度Eclipseが終了します。なんでじゃ!

これたぶん、LinuxやWindowsでは別プロセスとしてEclipseがもう一つ立ち上がるんだと予想。OS Xだと、同一アプリを複数プロセスで起動することができないので、こうなってしまっているんじゃないかな。

道具は思考を規定する

こうやって不満に思ったところを書き出してみましたが、これで分かったのは、私の考え方は完全にXcodeに支配されているということです。もうちょっと穏当な言い方をすれば、開発スタイルがXcodeに合わせて最適化されている、ってことでしょうか。もし始めからEclipseだけを使っていれば、それに合わせた思考体系になっていて、ここで挙がった不満は生じない、そもそもそういう欲求が起きないのでしょう。

そしてそれは、その道具を使って開発されるアプリにも影響を及ぼさないはずがないです。つまり、Xcodeの思考体系に慣れたプログラマと、Eclipseの思考体系に慣れたプログラマは、どうしたってそのスタイルに引きずられながらアプリを開発します。これは、ボタンのちょっとした挙動や、設定画面に必要な項目などで、ちょっとした、ほんとにちょっとした差として表れるでしょう。

それらが積み上がっていった結果、どちらがより良いアプリになるか?その辺りを意識しながら、Eclipseを使い続けてみようと思います。

 

先日まで短期のバイトが入ってました


先日まで短期のバイトが入ってました。プログラマバイトです。本当に短期で一週間にも満たなかったんで、お金を稼ぐというよりは、体験を目的とした感じです。

バイトだったり新人社員だったりした場合、うちの会社は仕事のやり方ってのがある程度決まっています。まず、新規のプロジェクトをお願いする事はなくて、既存のプロジェクトに参加してもらいます。そして、あるクラスのある機能の実装をお願いします。そのときに、大体この辺のメソッドをいじればいいんだよ、という事は伝えます。作業時間の目安は2時間程度。そのくらいで出来る規模です。出来上がったら、コードレビューして確認します。

このコードレビューが肝ですね。レビューは、いつも2時間くらい。つまり、実装時間と同じくらいかけます。書いてもらったコードを、ああでもないこうでもないと突き回して、ちょっと書き直させてもらうねと言って、痕跡が残らないくらい書き換えてしまいます。プログラマにとっては、自分の書いたコードをリライトされることほどの屈辱はないですよね。レビューという名を借りて公開処刑をやっているんじゃないか、という気持ちにもなります。

いろんな人の書いたコードを見てきましたが、大体みんなロジックは間違えてないんですよ。プログラミング原語は理解している。UI Kitの使い方もオッケー。アルゴリズムも悪くない。

問題は、「それをどこに書くか」ということです。つまり、どのクラスで、どういうメソッドを切るか、というところに問題の大部分が集中します。経験の浅いプログラマの場合、多くの場面でメソッドの切り方が不適切です。不必要に細かすぎたり、同一処理が分散したりしています。レビューでは、そこを指摘して、メソッドの切り直しを行います。ロジックやアルゴリズム自体は変わらないので、コピー/ペーストで済みます。これを行うと、コードが見違えるようになり、デバッグや拡張が容易になります。

じゃあ如何にして適切なメソッドの切り方を身につけるか? いろいろ考えますが、やっぱり経験しかないですかね。個人的な意見では、適切なメソッドさえ切れれば、プログラミングにまつわる多くの問題は解決します。世の中にはユニットテストとかいう類いのものがありますが、あれはメソッドが適切に切られているということを前提としていますよね。そこが不適切だったら、テストなんか無意味です。本当は、メソッドの切り方が適切かどうかテストする手法が欲しいんですが、それはまだないでしょう。

どうにかしてここを定性化できないか、と思って書いたのがオートマ本だったんですけど、うーん、物足りないですね。ソースコードの質を上げるには、レビュアーの質に頼るしかないのか、というのが現時点での結論です。

『絶対に挫折しないiPhoneアプリ開発「超」入門』が改訂


ここ半年ほどしばらくおとなしくしていましたが、またぞろ、うちの事務所に人が入れ替わり立ち替わりやってくるようになりました。やっぱり人の動きがあると楽しいね。エンジニアのバイトも、案件ごとにお願いしてたりします。

tektek_book

さて、個人的に昔からつきあいのある、TEKTEKの高橋京介さんの著作である『絶対に挫折しないiPhoneアプリ開発「超」入門』が、改訂されましたのでご紹介。ご本人から献本いただきました。改訂版では、iOS 7に完全対応。スクリーンショットなども、すべて差し替えられています。

しかし紙の技術本って、対象ソフトの新しいバージョンが出るたんびにスクリーンショット差し替えて改訂版出してって、なんとかならんのですかね。前の本を買った人は置いてきぼりですからね。こういうところこそ、電子書籍の出番だよなー、と思います。

NSViewAnimationにcompletionを付ける


最近は、iOSプログラミングとOS Xプログラミングが半々の日々です。

OS XのApp Kitでアニメーションをしようとすると、パッと思いつくのはCore Animationフレームワークです。iOSでもおなじみの、CALayerを使ってアニメーションします。Layer-backedアニメーションってやつですね。

このCALayerによるアニメーションは、パフォーマンスもよく、様々なエフェクトもかけられるし、サポートクラスもたくさんあって、とても便利なんですけど、アニメーションさせてももとのビューが動かない、という特徴があります。見せかけだけなんですね。なので、アニメーション終了後にビューのframeを設定し直したりします。

でも、アニメーションの種類によっては、frameを動かす事が重要なものがあります。たとえば、テキストビューのアニメーションです。テキストビューの大きさが変わるアニメーションの場合、その動きに合わせて内部のテキストの配置も変わってほしいですね。これをCALayerでやろうとすると、単純にテキストが拡大/縮小するだけになってしまいます。アニメーションのステップごとに、テキストビューを毎回redrawしてやらないといけないんですよ。

そこで登場するのが、NSViewAnimation。App Kitのアニメーション周りで最初に登場した古いクラスです。こいつは、ビューのframeの大きさを変えてくれます。たぶん、タイマー回してframeの値を順々に変えているだけの、単純なものです。パフォーマンスはよくないですが、求めに応じて使い分けます。

でも、古いクラスなので、あんまり使い勝手がよくないです。いちばん面倒なのが、アニメーションの終了通知がdelegateなこと。最近はブロック記法にすっかり慣れてしまったので、completionブロックを呼び出してほしいところです。

ということで、そこだけ拡張してみました。NSViewAnimationのサブクラスとして、HMViewAnimationというのを作り、アニメーションが終わったらcompletionを呼び出すanimateWIthAnimations:duration:completion:というメソッドを追加してみました。

宣言はこんな感じ。

typedef void (^HMViewAnimationCompletion)(void);

@interface HMViewAnimation : NSViewAnimation

// Property
@property (nonatomic, copy) HMViewAnimationCompletion completion;

// Initialize
+ (void)animateWithAnimations:(NSArray*)animations
duration:(NSTimeInterval)duration copmletion:(void (^)(void))copmletion;

@end

実装はこうです。

@implementation HMViewAnimation

+ (void)animateWithAnimations:(NSArray*)animations 
        duration:(NSTimeInterval)duration copmletion:(void (^)(void))completion
{
    // Create view animation object
    HMViewAnimation*    viewAnimation;
    viewAnimation = [[HMViewAnimation alloc] initWithViewAnimations:animations];
    [viewAnimation setDuration:duration];
    [viewAnimation setDelegate:viewAnimation];
    viewAnimation.completion = completion;

    // Start animation
    [viewAnimation startAnimation];
}

- (void)animationDidEnd:(NSAnimation*)animation
{
    // Invoke completion
    if (_completion) {
        _completion();
    }
}

@end

簡単ですね。トリッキーなことは何もしていませんが、なかなかに便利です。

クラスクラスタのプリミティブメソッドの見つけ方


「The Complete Friday Q&A: Volume I」を読んでます。今日読んだのは、「Subclassing Class Clusters」の項。

Objective-CというかFoundationには、クラスクラスタと呼ばれる仕組みがありまして。これは、同じAPIを使って、異なる実装を自動的に使い分けるための仕組みです。

たとえば、NSStringはクラスクラスタとして提供されています。それが何を意味しているかというと、NSStringのインスタンスを作っても、実はそれはNSStringじゃなくて、NSCFStringやNSPathreStore2っていうクラスになっているんですね。いわば、NSStringは抽象クラスで、自動的に効率のいい実装を持つクラスが選択されています。デザインパターンでいうと、Factory Methodパターンみたいなものです。言語仕様でのサポートというよりは、Foundationで実現しているものです。

クラスクラスタは、提供されているものを使う分には楽しく使えるんですけど、サブクラス作ろうとすると、とたんに面倒になります。まずはまるのが、既存のNSStringの挙動を変えてやろう!と思ってNSStringのサブクラスを作ると、うまくいきません。これは、具体的な実装である、NSCFStringなどに対してサブクラス化やメソッド差し替えを行わないといけないです。

その次には、自前の実装クラスを作って、クラスクラスタに追加してやろう!ということを考えるでしょう。この場合は、NSStringのすべてのメソッドを実装する必要はなく、プリミティブメソッドと呼ばれるものだけをやればいい、ということになっています。他のメソッドは、最終的にはこのプリミティブメソッドを呼び出して、具象クラスにアクセスします。

で、ここからがFriday Q&Aのネタなのですが、このプリミティブメソッドをどうやって見つけるのか?基本は、APIドキュメントを読みます。確かに書いてあるのですが、特別なセクションがある訳ではなく、普通の説明の流れにちょろっと書いてあるだけなので、見落としがちです。

そこで紹介されているもう1つの方法が、ヘッダファイルのinterfaceセクションに宣言されているメソッドがプリミティテブである、というものです。それ以外のものは、カテゴリとして宣言されているのです。これも、言語仕様ではなくてマナーの範疇なので100% sureではないですが、充分に役に立ちそうです。

その観点が面白いので、めぼしいクラスのintefaceで宣言されているメソッドを抜き出してみました。

NSArray

- (NSUInteger)count;
- (id)objectAtIndex:(NSUInteger)index;

NSMutableArray

- (void)addObject:(id)anObject;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)removeLastObject;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;

NSAttributedString

- (NSString *)string;
- (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range;

NSMutableAttributedString

- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str;
- (void)setAttributes:(NSDictionary *)attrs range:(NSRange)range;

NSData

- (NSUInteger)length;
- (const void *)bytes NS_RETURNS_INNER_POINTER;

NSDate

- (NSTimeInterval)timeIntervalSinceReferenceDate;

NSDictionary

- (NSUInteger)count;
- (id)objectForKey:(id)aKey;
- (NSEnumerator *)keyEnumerator;

NSMutableDictionary

- (void)removeObjectForKey:(id)aKey;
- (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;

NSString

- (NSUInteger)length;
- (unichar)characterAtIndex:(NSUInteger)index;

NSMutableString

- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)aString;

NSValue

- (void)getValue:(void *)value;
- (const char *)objCType NS_RETURNS_INNER_POINTER;

 

なるほど。これらがプリミティブ的に扱われている訳ですね。NSDateがtimeIntervalSinceReferenceDateだけ宣言されているのとか、面白いですね。

「The Complete Friday Q&A: Volume I」を買いました


The Complete Friday Q&A: Volume I」という本を買いました。iBooks Storeで。名前からは分かりにくいですが、Objective-Cに関する本です。

ちょっと古い本で、2011年に出たそうです。実際に書いてある内容は、筆者のBlogで2008年から2010年まで掲載されていたものなので、iPhone登場以前のOS XでのObjective-CおよびCocoaの話です。でも、いまでも問題なく通用しますよ、たぶん。

内容は、Objective-Cランタイムや、OS Xの低レベルの動作の話。僭越ですが、私の「ダイナミックObjective-C」と似たようなところをターゲットとしていますね。目次を一部抜き出してみましょう。

  • Multithreaded Programming and Parallelism Overview
  • Blocks in Objective-C(2008年の話!)
  • Interprocess Communication
  • Code Injection
  • Operations-Based Parallelization
  • Using the Clang Static Analyzer
  • Intro to the Objective-C Runtime
  • Code Generation with LLVM
  • Mac OS X Process Memory Statistics
  • Intro to Grand Central Dispatch
  • A GCD Case Study: Building an HTTP Server
  • NSRunLop Internals
  • Stack and Heap Objects in Objective-C
  • Subclassing Class Clusters

おおお、面白そうだ!こんなリストに反応してしまうのは、自分でもやっぱりおかしいとは思いますが。久しぶりにじっくり読めそうな本を見つけたので、しばらく読み込んでみたいと思います。

arm64対応でObjective-Cランタイムは速くなるのか?


Appleが、2月1日以降はiOS 7への最適が必須である、という話を去年の暮れにしていました。うちはだいたい移行は終わったんですけど、そういえばarm64対応ってのが残ってたな、と思い出しました。

そういえば、これも去年に、iOS 7でarm64対応するとObjective-Cランタイムレベルで速くなる、という話をどっかで聞いたような気がしました。あらためて検索すると、この話ですね。「Hamster Emporium: [objc explain]: Non-pointer isa

普通に考えると、64bit化しても恩恵を受けるのはムービーとかのでかいデータを扱うアプリだけで、多くのアプリはそんなに影響ないです。でも、Objective-Cランタイムが改善されるとなると、話が別です。すべてのアプリが恩恵を受ける事ができますね。件の記事では、アドレスが64bit化されることで余分なビットが生まれるので、isaがポインタではなくなって自身の中に入ってしまうと。これにより、retain countなんかのアクセスが速くなって、ランタイムの速度が上がる、という主張みたいです。

これも一度時間をとって検証してみたいですね。

iOS 7でCGBitmapContextに対する描画が変わった?


iOSでは、高速なグラフィックを実現するために、GPUの機能を積極的に利用しています。その恩恵を受けるには、UIViewが作成したCGContextRefに対してCore GraphicsのAPIを使う事が必要になります。ここで行われる描画は、オフスクリーンのビットマップに対するものとだいぶ違ったものになります。

何が違うのか、分かりやすい例として矩形の塗りつぶしを考えてみます。矩形の塗りつぶしは、もしオフスクリーンバッファに対して行うとすれば、一定領域へのメモリのセットになるんですね。たとえば、白で塗りつぶすとしたら、0xffffffでメモリ領域を埋める事になります。

スクリーンが小さいうちはこれで問題ないのですが、最近のiPad Retinaとかだと大変です。スクリーンサイズは、1536×2048。ということはオフスクリーンバッファを作るとすると、1536x2048x4byte = 12MByteということになります。12MBの領域をmemsetするとなると、これ時間かかるんですよ。iPad実機だと、1秒から2秒くらいかかります。これでは高速な描画なんかできませんね。

Core Graphicsには、矩形領域塗りつぶしのための、CGContextFillRectというAPIがあります。これの呼び出しは、時間はかかりません。ということは、このAPIではメモリのセットはやっていないはずです。おそらく、「矩形塗りつぶし」という手続きだけを覚えておいて、画面描画時にOpenGLテクスチャを作成していると予想されます。これなら充分に速くなります。

CGContextは、UIViewが用意するもの、つまりdrawRect:で取得できるものの他に、オフスクリーンバッファ用に作る事もできます。CGBitmapContextっていうものです。これで作ったCGContextは、Core GraphicsのAPIを使えるのですが、パフォーマンスでは大きく見劣りします。実際、CGContextFillRectの呼び出しにえらい時間がかかります。だから、iOSではCGBitmapContext使わないようにしましょうね。

…という話だったんです。iOS 6までは。

それが、iOS 7ではどうも変わったような気がするんです。CGBitmapContextに対するCGContextFillRectの呼び出しが、すごい速くなっているみたいです。もしかして、メモリセットしないようになった?でもそれだと、いろいろと破綻しねぇ?オフスクリーン使っているから、遅くてどうしようもなくて放っておいたコードがあるんですが、iOS 7で動かしたら速くなっていてビックリしているところです。いま、検証しようとしています。

iOSでは何故responder chainは重要でないのか


最近はOS Xプログラミングもよくやっているんだけど、iOSではあまり気にしないけど、OS Xではとっても意識するものは、responder chainですね。responder chainについては、Appleのドキュメント「Event Delivery: The Responder Chain」を参照してください。または、昔に書いた「ダイナミックObjective-C (89) デザインパターンをObjective-Cで – Chain of Responsibility (1)」もあわせてどうぞ。

どういうものかというと、普通iOSでボタンにターゲットとアクション設定するときは、addTarget:action:forControlEvents:を使うでしょ。このとき、ターゲットとなる第一引数に特定のオブジェクトじゃなくて、nilを指定します。そうすると、ボタンを押したときのアクションは、そのイベントを受け取ってくれる人(actionに対応するメソッドを持っている人)まで数珠つなぎに送られていくのです。これが、反応の連鎖をたどる、ということでresponder chainって呼ばれます。

OS Xプログラミングをやっていると、このresponder chainをとてもよく使うんですよ。なんでかなと考えてみると、OS Xで何らかの操作を行うときは、まず対象をマウスで選択して、メニューからコマンドを実行する、という流れになります。このとき、操作対象とメニューのコマンドには、強い結びつきがないんですね。たとえば、コピーというコマンドは、いろんな対象に対して送られる可能性があります。

それに対してiOSでは、多くの場合1つのタッチ操作で、対象の選択とコマンドの実行を実現しています。つまり、対象とコマンドが一対一関係ですね。その方が直感的でよろしいです。でも、機能が多くなってくると、「対象×コマンド」の数だけボタンを用意しなくてはいけないので、無理が出てくるんですよね。

ということで、マウス/キーボードのインタフェースと、タッチのインタフェースとでは、自ずとイベントハンドリングのプログラミングスタイルが変わってくるんです、という話でした。

OS Xファーストの開発スタイル


去年の末から、うちの会社では、「OS Xファースト」のスタイルで開発するものが増えました。開発するときに、まずOS Xで動かす。その後で、iOSに移植する、というやり方です。これがねぇ、とてもいいんですよ。

このスタイルになった一番の理由は、ドキュメント・ベース・アプリケーションのものが増えたからです。書類を作成するタイプのアプリ、つまりはエディタタイプのものですね。エディタものをiOSで作ろうとすると、色々面倒なんですよ。というより、OS Xの方が圧倒的にエディタに向いているんです。理由は以下の通りです。

標準のファイルブラウザが使える

OS Xには、ファイルシステムに存在するファイルを確認および操作するファイルブラウザがあります。何の事はない、Finderのことですね。忘れがちですが、iOSにはFinderに相当するものがありません。

エディタアプリだと、書類を作成するんです。そうすると、ファイルの確認や操作が必要になるんですよ。OS XだったらFinderでひょいっとのぞけばおしまいなんですが、iOSだとソースコード書き連ねてアプリ側でやらないといけない。メンドクサイ。

あと、オープンダイアログ。OS Xには標準のオープンダイアログがありますが、iOSにはないんです。だから、まず始めにオープンダイアログを自作しないといけない。超メンドクサイ。

プルダウンメニューが使える

エディタアプリは、どうしても操作のためのコマンドが多くなります。これは複雑というよりは、単に求められるものが多いんですね。たとえば、アンドゥでしょ、リドゥでしょ、カットにコピーにペーストでしょ、削除でしょ、すべてを選択でしょ…。これらをボタンとして並べていったら、iPhoneの画面がそれだけでいっぱいになってしまいます。そうならないために色々とUIを考えるんですけど、そっちにやたら時間を取られるんですよ。

でもこれがOS Xならば!プルダウンメニューがあるじゃないか!プルダウンメニューの素晴らしいところは、プルダウンできるところです!当たり前すぎて何言ってるか分からないですが、つまりいつもは隠れていて、必要なときにクリックして引き出す事のできるUIということですね。これは、多くの項目を並べるのにとても適したものなんです。

だから開発の初期段階では、とりあえずコマンドの呼び出しはメニューだけにして、中核機能だけ先に作っちゃえということができます。キャッチーでファンタスティックでバブリーなUIは、後でじっくり考えればいいのです。

キーボードが使える

エディタアプリでは、文字を入力する事が多いです。文字の入力には、そりゃなんだかんだいってもキーボードでしょう。キーボードを使うのがいちばん効率いいです。

ユーザの使い勝手だけでなく、プログラムの立場からでもそうです。iOSでは文字入力時にソフトウェアキーボードが表示されますが、あれが出てくると画面が狭くなるんですね。となると、動的にビューの大きさ変更しないといけないし、狭い画面でも使えるUIにしないといけない。うわー、メンドクサイ。

こういった理由から、まずはOS Xで動くものを作っています。もちろんプロトタイプの使い捨てではなくて、きちんと世の中に出せるレベルまで持っていきます。始めにきちんと設計しておけば、かなりのソースコードをOS XとiOSで共有できます。モデルだけではなく、コントローラクラスも共用できます。OS XではNSViewControllerのサブクラス、iOSではUIViewControllerのサブクラスとして、同じクラスに実装していきます。#ifdefだらけになりますが。

ただこうしてOS X版を作っても、リリースするかどうかはまた別の話ですね。出したとしてもどれだけ売れるのやら、という問題がありますから。