原文链接:点击打开链接
在我之前的文章中,我讨论了id什么时候会被提升到instancetype.但是现在我要解释这个,我想解释清楚为什么你应该明白但是不要依赖它(id).相反,你应该立刻使用instancetype
让我们来从这些粗体介绍开始,然后我会返回并且解释它:
当一个类返回相同类的实例的时候,在适当地时候使用instancetype
首先进行一些定义:
@interface Foo:NSObject
- (id)initWithBar:(NSInteger)bar; // initializer
+ (id)fooWithBar:(NSInteger)bar; // convenience constructor
@end
对于一个便利构造函数,你应该总是使用instancetype.编译器不会自动将id类型转换为instancetype.
当你在初始化的时候输入这些会更加难理解:
- (id)initWithBar:(NSInteger)bar
编译器会这样代替:
- (instancetype)initWithBar:(NSInteger)bar
这对于ARC来说是十分必要的.这也是为什么人们告诉你没必要用instancetype,尽管我强烈主张你应该用它.剩下的原因取决于这些:
这些是三个优点:
1. 明确的.你的代码就像它描述一样运行,而不是一些其他的
2. 模式.你在它无数次运行的时候建立一个良好的习惯
3. 一致性.你的代码确立了一致性,让它更易读
明确的
从事实上来说,在技术层面上从init返回instancetype没有任何技术上的好处,但这是由于编译器自动的将id转换为instancetype.你越来越依赖这种怪癖:当你写init返回id类型的时候,编译器会将它解释为返回的是instancetype型.
这些对于编译器来说是等价的:
- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;
在你眼中他们不等价,充其量你会忽略他们的不同并且略过它.这是你不能学会忽略的东西.
模式
尽管init和其他方法没有区别,但是当你定义一个便利构造函数时就会有区别
这两个就不等价:
+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
你想要第二种形式的.如果你的构造函数返回的是instancetype类型,你将会每次都得到正确的结果.
一致
最后,想象一下你想要init函数,和一个便利构造函数,如果你把他们放到一起.
如果你对init使用id作返回值,你最后的代码会这样:
- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
但是如果你用instancetype,代码会变成这样:
- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
这会更加一致并且易读.它们的返回类型相同,并且十分明显
总结
除了在旧编译器上写代码,你应该适当地使用instancetype
在你写下返回值为id类型的时候你应该犹豫一下,问一下你自己:是要返回这个类的实例?如果是这样,就是instancetype类型
有很多情况是你还需要返回id类型,即如果你返回的是不同的类.
但是你应该仍然会使用instancetype类型频于id类型