Objective-C 中 id 和 instancetype 的使用与异同
今天记录一下在Objective-C中,保留字id
和instancetype
各自的用法,以及异同。
id
什么是id类型
在概念上,id
类似于Java中的Object
类,可以转换为任何数据类型。换句话说,id
类型的变量可以存放任何数据类型的对象。在内部处理上,这种类型被定义为指向对象的指针,实际上是一个指向这种对象的实例变量的指针。
下面是id
类型在Objective-C中的定义:
typedef struct objc_object {
Class isa;
} *id;
从上面看出,id
是指向struct objc_object
的一个指针。也就是说,id
是一个指向任何一个继承了Object
(或者NSObject
)类的对象。
id的用法
id是一个指针
因为id
类型是一个指针,所以我们在使用id
类型的时候不需要添加任何的星号,例如:
id foo = nil;
上面的代码声明了一个id
类型的指针foo
,这个指针指向NSObject
的任意一个子类。
而下面的代码则定义了一个指针,这个指针指向另一个指针,被指向的这个指针指向NSObject
的一个子类。
id *foo = nil;
可以接收任何消息
在ObjeciveC中,id
是可以接收任何消息的,所以我们可以将一个方法或者一个变量声明为id
类型,用来接收多种类型的数据。
例如NSArray
中即可以接收NSString
,也可以是NSObject
,这时候就需要用id
了。
小结
在我的理解看来,id
类型就是一个万能的类型,当我们暂时不知道方法的返回值该是什么类型的时候,我们可以先尝试将方法定义为id
类型,等待确定方法返回值后再回来修改,或者就直接定义为id
类型罢了。
或者我们可以定义一个id
类型的变量,用它来做中间变量,用来接收多种类型的数据。
关联返回类型
在介绍instancetype
之前先讲解一个概念,就是关联返回类型的方法。
根据Cocoa的命名规则,满足下述规则的方法:
- 类方法中,以
alloc
或new
开头 - 实例方法中,以
autorelease
,init
,retain
或self
开头
会返回一个方法所在类 类型 的变量,这里我用空格和加粗断句,方便大家理解。也就是说,这些方法的返回值的类型,是调用这些方法的那个类的类型。NSArray
调用的alloc
方法,即[NSArray alloc]
。那么这个方法的返回值就是NSArray
类型的。同理[[NSArray alloc] init]
返回的也是NSArray
类型的值。
那么说回来,以上这些的方法呢,我们就称之为关联返回类型的方法。
instancetype
什么是instancetype类型
instancetype
是从 clang 3.5 开始提供的一个关键字,表示某个方法返回的未知类型的Objective-C对象。单看这个定义,感觉和id
还是挺像的。
instancetype的作用
当我们有一个自定义的方法,例如下面这个:
@interface UIView (TestView)
+ (id) testFounation;
@end
TestView
类是UIView
类的分类,当我们如下调用testFounation
方法时
[TestView testFounation];
其返回值的类型和方法的声明相同,是id
类型。
而如果我们如下使用instancetype
声明testFounation
的话
@interface UIView (TestView)
+ (instancetype) testFounation;
@end
当我们再次调用testFounation
方法:
[TestView testFounation];
其返回值的类型会是TestView
类型。
小结
综上我们可以看出,instancetype
的作用实际上就是使非关联返回类型的方法返回所在类的类型。
通俗的讲,就是当我们需要方法的返回类型不是未知的id
类型,而是具体的,调用该方法的类的类型的时候,我们要使用instancetype
类型。
再具体一些,当我们自定义init
方法的时候,其返回值就是instancetype
类型。
id和instancetype的异同
总结下来,id和instancetype的异同一共有以下几点:
相同之处
其都可以作为方法的返回类型。
不同之处
instancetype
可以返回和方法所在类相同类型的对象,id
只能返回未知类型的对象。instancetype
只能作为返回值,不能像id
那样作为参数。instancetype
只适用于初始化方法和便利构造器的返回值类型。- 在不同的内存管理机制中:
- 在ARC(Auto Reference Count)环境下:
instancetype
用来在编译期确定实例的类型,而使用id的话,编译器不检查类型, 运行时检查类型。 - 在MRC(Manual Reference Count)环境下:
instancetype
和id
一样,不做具体类型检查。
- 在ARC(Auto Reference Count)环境下:
关于第三点,因为我现在还没有具体学习到内存管理相关的知识,可以参考下方参考中三木成森的文章,有举例说明该点。