先日Xcode 4.3がリリースされました。Xcode 4.3ではclangのバージョンが3.1となり、instancetypeという型をサポートするようになりました。

返り値のid型の推論

instancetypeの話の前に、id型の返り値について考えます。Cocoaでは、+ alloc- initといったメソッドは軒並みid型の値を返すようになっています。基本的に、id型の値はどのようなオブジェクト参照型にも代入できることになっています。

しかし、clangは以下のようなコードに警告を発します。

NSString *myBlog = [[NSURL alloc] initWithString:@"http://takebayashi.asia/"];

+ alloc- initWithStringも返り値がid型なので本来ならばNSString *型の変数に代入しても文句は言われないはずですが、このようなメソッドはレシーバと同じクラスのオブジェクトを返すことが慣例となっているため、clangはこれらのメソッドの返り値がNSURL *であると推論します。これによって、上記のような警告が発せられることになります。

instancetype型

instancetype型は、上で述べたような「レシーバのクラス==返り値オブジェクトのクラス」となることを明示的に示すための、メソッドの返り値にのみ使える型です。例えば次のコードでは、+ (Clazz *)defaultObject;と書いても同じ意味となります。

@interface Clazz : NSObject
+ (instancetype)defaultObject;
@end

これだと何の役に立つのかわからないでしょうから、今度はプロトコルを使った例を示します。

@protocol ConvenienceConstructor
+ (instancetype)defaultObject;
@end
@interface Clazz : NSObject <ConvenienceConstructor>
@end

このコードにおいて、ConvenienceConstructorプロトコルで定義されたメソッドには特定のクラス名は書いてありませんが、返り値の型がinstancetypeとなっているため、Clazzクラスの同名メソッドの返り値はClazz *型であることが推論されます。

このように、返り値としてinstancetype型を用いることで他言語でいう共変戻り値みたいなことができるようになります。また、メソッドの返り値以外の場所では使用できないので、その点にも注意が必要です。