在开发当中,id和instancetype都是我们常见的类型,那么这两者有什么异同点呢?
相同点
我们知道,id实际上是指向objc_object结构体的指针,如下:
// A pointer to an instance of a class.
typedef struct objc_object *id;
也就是说,id类型的指针指向的是objc_object结构体,而objc_object结构体表示的就是类对象的实例对象,所以id可以表示所有类型的实例。
而instancetype也是一个可以指向所有类型的实例变量指针。
不同点
我们在项目开发过程中,一般是覆写或者自定义一个类的实例对象的初始化方法的时候,才会用instancetype作为返回值类型,除此之外,在其他的地方根本不会用到instancetype。如下所示:
而id既可以用在实例对象的初始化方法中表示返回类型,又可以表示各个方法中的参数实例的类型。也就是说, id 可以在任何地方使用用以表示各种未知类型的对象。
那么,为什么在实例对象的初始化方法中必须使用instancetype作为返回值类型呢?
我们来看一个例子。
新建一个Person类,然后定义其实例对象的初始化方法:
//Person.m
//id作为返回值类型
+ (id)person {
return [[self alloc] init];
}
- (id)init {
if (self = [super init]) {
NSLog(@"id");
}
return self;
}
//instancetype作为返回值类型
+ (instancetype)person {
return [[self alloc] init];
}
- (instancetype)init {
if (self = [super init]) {
NSLog(@"instancetype");
}
return self;
}
我们分别以 instancetype 和 id 作为返回值定义了初始化方法,然后在外界新建Person类的实例对象,分别编译:
/*
id:编译通过,不会用警告
instancetype:提示警告,接收对象的指针类型不匹配
*/
NSString * person = [Person person];
我们发现,使用NSString类型的指针指向Person类型的对象,当以id作为返回值类型的时候,编译通过,因为id可以指向任意类型的对象,id在运行期才会确定对象的真实类型;当以instancetype作为返回值类型的时候,编译期间会报警告,提示接收对象的指针类型不匹配,所以说instancetype比id多了一个在编译期通过编译器检测变量真实类型的功能,这可以提前暴露程序存在的风险。
在编译期,编译器会检索对象指针instancetype的真实类型;而id类型的实例变量是在运行期才会确定其真实类型,所以编译期即便是类型不匹配也不会报警告。因此,使用instancetype作为返回值,实际上也是提高程序健壮性的一个手段。
以上。