カテゴリー : 2014年 1月

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


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

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

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

今週の笑っていいともの『大辞泉』コーナー


今週も、フジテレビの笑っていいとも!で、「国語辞典をアップデート〜目指せ言葉の達人」が放送されました。小学館の、ロマンスグレーがナイスな板倉編集長が出演中です。

今日の番組では最初に、先週達人の言葉に選ばれた、木下優樹菜の「大人」の意味である「1年365日を異常に早く感じてしまう人。」が、大辞泉に収録される事が発表されました!次の辞書データアップデートに含まれるという事です。マジすか。自分の仕事に関する事がテレビで発表されると、ビックリです。

今日のお題は「結婚」。達人として選ばれたのは、タモリと再び木下優樹菜。なんか、優樹菜に甘いですよ、板倉さん。

iitomo0124

そんな自分で考えた言葉の投稿もできる、iOSアプリ『大辞泉』をお試しください!

『大辞泉』1.6.1がリリース


daijisen_icon

iOSアプリ『大辞泉』1.6.1がリリースされました。変更内容は、以下の通りです。

[機能の変更]

  • ライセンス契約の問題により、動画再生の条件を、「利用回数制限の解除」または「利用回数の最大値を50に拡大」を購入していること、に変更

[不具合の修正]

  • 語釈の表示が見切れる事のある問題の修正
  • 検索一覧結果画面、語釈表示画面から動画が再生できない問題の修正

「笑っていいとも!」に大辞泉の編集長が登場!


itakura本日先ほどテレビで放送された、フジテレビの「笑っていいとも!」で、小学館の大辞泉編集長である、板倉峻さんが登場しました。新しいコーナーである「国語辞典をアップデート〜目指せ!言葉の達人〜」の審査役としてです。

この「目指せ!言葉の達人」のコーナーは、辞書に載せるような言葉の意味を、いいとものレギュラー陣たちが考えるものだそうです。前に小学館の大辞泉でやられていた、「あなたの言葉を辞書に載せよう。」キャンペーンのテレビ版ですね。

小学館のキャンペーンで、一般から公募された言葉は、iOSアプリ「大辞泉」で確認する事ができます。また、このアプリには、自分で考えた言葉の意味を投稿してみんなに公開できる「みんなのコトバ」機能が搭載されています。この機会に、ぜひ試してみてください。

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


「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で動かしたら速くなっていてビックリしているところです。いま、検証しようとしています。