今天记录一下在Objective-C中,保留字idinstancetype各自的用法,以及异同。


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的命名规则,满足下述规则的方法:

  1. 类方法中,以allocnew开头
  2. 实例方法中,以autoreleaseinitretainself开头

会返回一个方法所在类 类型 的变量,这里我用空格和加粗断句,方便大家理解。也就是说,这些方法的返回值的类型,是调用这些方法的那个类的类型。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的异同一共有以下几点:

相同之处

其都可以作为方法的返回类型。

不同之处

  1. instancetype可以返回和方法所在类相同类型的对象,id只能返回未知类型的对象。
  2. instancetype只能作为返回值,不能id那样作为参数。instancetype只适用于初始化方法和便利构造器的返回值类型。
  3. 在不同的内存管理机制中:
    • ARC(Auto Reference Count)环境下:instancetype用来在编译期确定实例的类型,而使用id的话,编译器不检查类型, 运行时检查类型。
    • MRC(Manual Reference Count)环境下:instancetypeid一样,不做具体类型检查。

关于第三点,因为我现在还没有具体学习到内存管理相关的知识,可以参考下方参考三木成森的文章,有举例说明该点。


参考

  1. Objective-C中的id类型——Kilnn
  2. 理解Objective C 中id——做个不善的人
  3. OC中instancetype与id的区别——三木成森
  4. Objective-C中的instancetype和id区别——kuizhang1