Objective-C中instancetype和id的区别

要区分instancetype和id,首先要弄清楚什么是关联返回类型(Related Result Type)。

关联返回类型即一个方法的返回类型就是调用这个方法的调用者的类型。具有下列条件的方法具有关联返回类型:

1 对于静态方法,方法名以alloc,new开头;

2 对于实例方法,方法名以autorelease,init,retain,self开头

关联返回类型的作用就是让编译器在编译期就可以进行类型检测(Type Check),比如下面的例子:

@interface X : NSObject

@property (assign, nonatomic) NSInteger index;

+ (id)create;

@end

@implementation X

+ (id)create {
    X *x = [[X alloc] init];
    
    return x;
}

@end


@interface Y : NSObject

/**
 *工程中如果不存在一个方法叫haha,即使create的返回值是id
 *编译器也会报错:No known instance method for selector 'haha'
 */
- (void)haha;

@end

然后进行如下调用:

[[X create] haha];

由于类X的create方法命名不符合关联返回类型的要求,因此编译器无法推断出该方法返回的就是一个X类型,而是只把返回值看成是id类型。又由于方法haha在工程中存在,因此可以成功编译。(但是运行时仍然会报错:unrecognized selector sent to instance)。

为了让不具备关联返回类型命名的方法也可以让编译器进行正确的推断,从而在编译期进行类型检查,就需要将返回的id类型换成instancetype。如果将上面例子中,把create方法的返回类型换成instancetype,编译器在编译的时候就会知道create方法返回的是一个X类型,而X类型没有声明haha方法,因此在编译器就会报错:

No visible @interface for 'X' declares the selector 'haha'

但是,与id不一样,id除了作为方法的返回类型,同时可以作为方法的参数以及声明变量,而instancetype只能作为方法的返回类型。

关于关联返回类型还有一点,如果子类override了父类的一个关联返回类型方法,那么子类的返回类型必须要么是子类本身,要么是父类(即可以兼容子类的类型)。

参考资料:

http://blog.csdn.net/kuizhang1/article/details/18048829

http://clang.llvm.org/docs/LanguageExtensions.html#objective-c-features

http://nshipster.com/instancetype/