前言
四年前Clang添加了关键字instancetype,目的在于取代-alloc和-init等方法的返回类型id,那么使用instancetype到底比id好在哪里?
instancetype宣言
不管何时,只要一个class要返回它相同的类实例,使用instancetype都是更加合适的。
我们知道,当调用类中(或者子类)的-alloc、-init或者class factory(+)方法,使用instancetype关键字会返回它的类实例。在这种情况下用instancetype而不是id有100个好处。
苹果也这样说:
In your code, replace occurrences of id as a return value with instancetype where appropriate. This is typically the case for init methods and class factory methods. Even though the compiler automatically converts methods that begin with “alloc,” “init,” or “new” and have a return type of id to return instancetype, it doesn’t convert other methods. Objective-C convention is to write instancetype explicitly for all methods.
更加有力的证据:Adopting Modern Objective-C
为什么?
首先你可能需要知道编译器会做什么
@interface Foo:NSObject
- (id)initWithBar:(NSInteger)bar; // initializer
+ (id)fooWithBar:(NSInteger)bar; // convenience constructor
@end
对于convenience constructor,你必须要使用instancetype,因为编译器不会把id自动转换成instancetype。
而对于initializer,当你的代码是这样的:
- (id)initWithBar:(NSInteger)bar; // initializer
ARC下,编译器会把它变成这样:
- (instancetype)initWithBar:(NSInteger)bar; // initializer
这也是为什么有人告诉你用instancetype是没有必要的,但是我建议你使用instancetype,原因有三:
- 明确性:你的代码含义明确,绝不会做其他事。
- 模式化:你建立了一个良好的习惯,这确实是很重要的。
- 一致性:让你的代码更一致,并且增加可读性。
1.明确性
在-init方法中返回instancetype确实是没有太多的好处,这是因为编译器会自动把id转换成instancetype。
这对于编译器来讲是等价的:
- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;
但是至少这在你眼中是不同的。
2.模式化
虽然对init或者其他方法,返回id和instancetype没什么不同,但是在convenient constructor(+)方法中他们是有区别的:
+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
你必须要用第二条才能保证你每次运行的结果正确,为什么?
考虑这样的情况:
然后我们这样调用:
发现x出现编译警告,因为我们的+factoryMethodA返回的是instancetype,它表示的是MyObject,因为MyObject没有-count*方法,所以出现了编译警告。
然而,因为我们的+factoryMethodB返回的是id,编译器没有在y行给警告,因为Objc语言动态类型和动态绑定的特性,id可能是任何class,又因为-count方法可能于存在某个类的某个地方,因此对于编译器而言,+factoryMethodB方法返回的东西是可能实现-count方法的。
3.一致性
在init中可以返回id,因此你可能会这样写:
- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
但是如果你使用instancetype,结果会是这样:
- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
这样看起来更加一致并且可读性更高。他们返回同样的东西,但是后者看起来更明显,不是吗?
结论
当要返回id类型的时候,你应该思考它是否会返回一个类的实例,如果是,请使用instancetype。
参考链接:
Done
作者 @biggergao
2016年05月18日