2013年2月2日土曜日

instancetype で generics の夢をみる?

instancetype は clang 3.1 から導入されたキーワード。


こう書いていたのを
@interface Person
+ (id)personWithName:(NSString *)name;
@end
こう書ける
@interface Person
+ (instancetype)personWithName:(NSString *)name;
@end

instancetypeを使うことでコンパイラに戻り値の型を推論させて型チェックが行えるようになる。

例えば
@interface Sample : NSObject
+ (id)factory1;
+ (instancetype)factory2;
@end
これを使ってこう書く
NSArray* s1 = [Sample factory1];
NSArray* s2 = [Sample factory2];
するとコンパイラは s2の方でワーニング出す。s1は戻り値がidなので何にでも代入できるが、s2はinstancetypeにより型が特定できるのでNSArrayとの型の違いがコンパイラによって指摘されている。

Cocoaでも、例えば UICollectionViewLayoutAttributes などで使われ始めている。

UICollectionViewLayoutAttributes Class Reference
+ (instancetype)layoutAttributesForCellWithIndexPath:(NSIndexPath *)indexPath

* * * *

この記事ではinstancetype使ったgenericsっぽい実装を紹介している。例えばその実装を使うと NSURLのコレクションをこんな感じで扱える。
NSURL <MapCollection> *sites = (id)[NSURL mapCollection];
[sites put:[NSURL URLWithString:@"http://www.jonmsterling.com/"]
        at:@"jon"];
[sites put:[NSURL URLWithString:@"http://www.nshipster.com/"]
        at:@"nshipster"];

NSURL *jonsSite = [sites at:@"jon"]; // => http://www.jonmsterling.com/
sitesはNSURLのインスタンスではあるがNSURL型を保存するマップ型(NSDictionary)のコレクションとして使える。

この実装コードが下記ブログで解説されている。



実装はinstancetypeの利用の他、非形式プロトコル(NSObjectのカテゴリ)やメソッドフォワーディングを使い、型付けのされたコレクションを実現している。

先ほどのコードに続けてこんなコードも書ける。
NSString <MapCollection> *schemes = (id)sites.scheme.uppercaseString;
/* => { jon: "HTTP://",
        reddit: "HTTP://",
        foam_repo: "GIT://" }
 */

??..正直最初見たときには目が点になった...。実装を見れば実現方法がわかるのだが非常に面白い。sitesはNSURLのコレクションで、その要素1つ1つに対して schemeとuppercaseStringのメソッドを投げて、その結果をNSStringのコレクションとして受け取っている。

instancetypeはコレクションからオブジェクトを取り出す箇所で使われている。
@protocol OrderedCollection <nsfastenumeration>
- (instancetype)at:(NSUInteger)index;
- (void)put:(id)object;
@end

@protocol MapCollection <nsfastenumeration>
- (instancetype)at:(id)key;
- (void)put:(id)object at:(id)key;
@end

良い刺激になった。

(関連情報)instancetype


(関連情報)メソッドのフォワーディングは木下さんの記事がわかりやすい。実装を読み解くのに参考になる。


Objective-C の動的性質を生かすにはこれ。


0 件のコメント:

コメントを投稿