instancetype 和 id 都是万能指针,都可以指向一个对象,那么他们有什么区别呢。
1.instancetype 和 id 类型的区别:
- id 可以用来定义变量,可以作为返回值,可以作为形参。instancetype 只能用于作为返回值。
- id 在编译的时候不能判断对象的真实类型,instancetype 在编译的时候可以判断对象的真实类型。
第二点是我们讨论的重点,为此我们需要知道什么是关联返回类型。
2.关联返回类型
根据 Cocoa 的命名规则,满足下述规则的方法会返回一个方法所在类类型的对象,被称为是关联返回类型的方法:
- 类方法中,以 alloc 或 new 开头
- 实例方法中,以 autorelease,init,retain 或 self 开头
换句话说,这些方法的返回结果以方法所在的类为类型,请看下面的例子:
NSArray *array = [[NSArray alloc] init];
复制代码
按照 Cocoa 的命名规则,语句 [NSArray alloc] 的类型就是 NSArray* 因为 alloc 的返回类型属于关联返回类型。同样,[[NSArray alloc] init] 的返回结果也是 NSArray*。
3.instancetype 的作用
这里我创建了一个 Person 类,继承自 NSObject。
如果一个不是关联返回类型的方法,如下:
@interface Person : NSObject
+ (id)initObject;
@end
复制代码
那么我们初始化时:
[Person initObject];
复制代码
根据 Cocoa 的方法命名规范,得到的返回类型就和方法声明的返回类型一样,是 id。 但是如果使用 instancetype 作为返回类型,如下:
@interface Person : NSObject
+ (instancetype)initObject;
@end
复制代码
当使用相同方式初始化时,得到的返回类型和方法所在类的类型相同,是 Person*。 总结一下,instancetype 的作用,就是使那些非关联返回类型的方法返回所在类的类型。
4.instancetype 的好处
关联返回类型
首先我们调用如下方法:
[[[Person alloc] init] setFrame:CGRectMake(0, 0, 100, 100)];
复制代码
这里调用了 Person 中不存在的 setFrame: 方法,会报以下错误:
No visible @interface for 'Person' declares the selector 'setFrame:'
复制代码
由于 [[Person alloc] init] 返回的结果是 Person*,这样编译器就能够根据返回的数据类型检测出 Person 是否实现 setFrame: 方法,有利于开发者在编译阶段发现错误。
id
如果我实现 initObject 方法如下:
+ (id)initObject {
return [[Person alloc] init];
}
复制代码
并且调用:
[[Person initObject] setFrame:CGRectMake(0, 0, 100, 100)];
复制代码
我们 Conmmand+B 编译一下,发现居然没有报错。原因是 initObject 不是关联返回类型方法,返回的是 id 类型,编译器不知道 id 类型的对象是否实现了 setFrame: 方法,在编译期没有检测 id 的真实类型,也就不能够替开发者及时发现错误。
instancetype
我们把返回类型改为 instancetype,再次调用 setFrame: 方法并编译,报了和第一次一样的错。在编译期检测了 instancetype 的真实类型。
所以,使用 instancetype 能够帮助我们在编译器及时发现一些错误。