【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