【iOS】instancetype和id的区别

在很早之前编译器没有提供instancetype关键字,我们在写函数/方法返回值时都用的是id。clang编译器有了instancetype之后,让我们的代码能够更加严谨且高效。

instancetypeid的最大区别就是编译时期的类型检测,而且instancetype不能作为参数类型传递。

类型检测

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 声明类
@interface YBPerson : NSObject

@property (nonatomic, copy) NSString *name; // 姓名
@property (nonatomic, assign) NSInteger age; // 年龄

/**
* 初始化方法
* @param name 姓名
*/
- (id)initWithName:(NSString *)name;

@end

方式一(使用id):

1
2
3
4
5
6
7
8
9
10
11
12
@implementation YBPerson

// 通过姓名初始化
- (id)initWithName:(NSString *)name {
self = [super init];
if (self) {
self.name = name;
}
return self;
}

@end
  • 使用NSArray类型指针接收

    1
    2
    NSArray *person = [[YBPerson alloc] initWithName:@"idbeny"];
    NSLog(@"%ld", [person count]);
  • 编译,没问题

  • 运行,崩溃(因为YBPerson的实例没有找到count这个方法)

    1
    [YBPerson count]: unrecognized selector sent to instance 0x60000382ca40

方式一(改造):

  • 使用YBPerson类型指针接收

    1
    2
    NSArray *person = [[YBPerson alloc] initWithName:@"idbeny"];
    NSLog(@"%ld", [person count]);
  • 编译器警告

    1
    Incompatible pointer types initializing 'NSArray *' with an expression of type 'YBPerson *'
  • 运行,崩溃(因为YBPerson的实例没有找到count这个方法)

    1
    [YBPerson count]: unrecognized selector sent to instance 0x60000382ca40

方式二(使用instancetype):

1
2
3
4
5
6
7
8
9
10
11
12
@implementation YBPerson

// 通过姓名初始化
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
self.name = name;
}
return self;
}

@end
  • 使用NSArray指针接收:

    1
    2
    NSArray *person = [[YBPerson alloc] initWithName:@"idbeny"];
    NSLog(@"%ld", [person count]);
  • 编译器警告

    1
    Incompatible pointer types initializing 'NSArray *' with an expression of type 'YBPerson *'
  • 运行,崩溃(因为YBPerson的实例没有找到count这个方法)

    1
    [YBPerson count]: unrecognized selector sent to instance 0x60000382ca40

通过上面的案例说明,使用idinstancetype都可以作为返回值使用,instancetype可以在编写代码时就能检测类型匹配问题,而id是不可以的。

参数类型传递

instancetype作为参数类型

1
- (instancetype)initWithName:(instancetype)name;

编译报错

1
2
Expected a type
Type name requires a specifier or qualifier

id作为参数类型

1
- (instancetype)initWithName:(id)name;

编译,运行,都没有问题。所以,id可以作为参数的类型传递,instancetype不可以。