【iOS开发每日小笔记(八)】instancetype类型与ID类型

这篇文章是我的【iOS开发每日小笔记】系列中的一片,记录的是今天在开发工作中遇到的,可以用很短的文章或很小的demo演示解释出来的小心得小技巧。它们可能会给用户体验、代码效率得到一些提升,或是之前自己没有接触过的技术,很开心的学到了,放在这里得瑟一下。90%的作用是帮助自己回顾、记忆、复习。

今天打开NSArray.h文件,偶然间发现一个类型:"instancetype"

 1 + (instancetype)array;
 2 + (instancetype)arrayWithObject:(id)anObject;
 3 + (instancetype)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt;
 4 + (instancetype)arrayWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
 5 + (instancetype)arrayWithArray:(NSArray *)array;
 6 
 7 - (instancetype)init;    /* designated initializer */
 8 - (instancetype)initWithObjects:(const id [])objects count:(NSUInteger)cnt;    /* designated initializer */
 9 
10 - (instancetype)initWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
11 - (instancetype)initWithArray:(NSArray *)array;
12 - (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag;

不论是印象中还是平时自己自定义的类,都会使用"id"作为返回类型,那么这个instancetype是个什么东西,和id有什么异同呢?

经过一番搜寻资料以及自己的测试,归纳如下:

一:instancetype是什么?

引用Clang:“instancetype is a contextual keyword that is only permitted in the result type of an Objective-C method”

这说明了instancentype是一个关键字,用于Objective-C 方法的返回值。另外,instancetype的重要作用,就是这种instancetype类型的对象可以告诉编译器,该对象到底是什么类型对象。这样,编译器在编译阶段就可以通过类型判断,知道你的代码是否有问题。

二:instancetype如何用?

为了说明instancetype如何用,我们需要先知道一个前提:

我们知道NSObject的创建用到的alloc init函数返回值的类型是id。但是,其实这里的id是被“优化过的”,因为编译器知道是某个特定的类去调用的alloc、init。比如:[[NSString alloc] init];那么编译器知道此时返回的对象类型应该是一个NSString。但是类方法就没有这种待遇了。下面我们就来测试一下:

我创建了一个TestObject类。提供两种类方法。

1 #import <Foundation/Foundation.h>
2 
3 @interface TestObject : NSObject
4 
5 + (id)testObjectWithID;
6 
7 + (instancetype)testObjectWithInstancetype;
8 
9 @end
 1 #import "TestObject.h"
 2 
 3 @implementation TestObject
 4 
 5 + (id)testObjectWithID
 6 {
 7     return [[self alloc] init];
 8 }
 9 
10 + (instancetype)testObjectWithInstancetype
11 {
12     return [[self alloc] init];
13 }
14 
15 @end

接着,我分别使用这两种类方法类创建实例,并调用一个“addSubView:”的方法:

1 UIView *view = [[UIView alloc] init];
2 [[TestObject testObjectWithID] addSubview:view];
3 [[TestObject testObjectWithInstancetype] addSubview:view];

编译,提示第三行报错 No visible @interface for 'TestObject' declares the selector 'addSubview:'

原因是编译器遇到id类型,是不会判断该类型的变量是否响应“addSubview”方法的,只在运行时才会判断。而instancetype类型,编译器就可以判断出此时这个实例是TestObject类型,而TestObject类并没有“addSubview”方法,因此会报错。

如果单独运行:

1     UIView *view = [[UIView alloc] init];
2     [[TestObject testObjectWithID] addSubview:view];

编译可以通过,但是运行就崩溃。这说明,通过instancetype类型,可以一定程度上可以提早发现误调用方法的错误。

三:instancetype与id的不同。

那么既然instancetype比id保险,那么是不是以后id全部用instancetype替换就好了?答案是否定的!

因为instancetype是“is only permitted in the result type”,只能作为返回类型,不能用作函数参数等其他地方。

我的demo很简单,供参考,地址:https://github.com/pigpigdaddy/InstancetypeDemo

参考资料:

http://blog.eddie.com.tw/2013/12/16/id-and-instancetype/

http://www.iwangke.me/2013/01/06/instancetype-vs-id-for-objective-c/

http://www.cnblogs.com/zuozeing/p/3616782.html

http://stackoverflow.com/questions/16743494/instancetype-vs-class-name-for-singleton

原文地址:https://www.cnblogs.com/pigpigDD/p/3953862.html