原帖:http://zhuanlan.zhihu.com/Foundation/19569459
Steven Fisher:只要一个类返回自身的实例,用instancetype就有好处。
- ( id )initWithBar:(NSInteger)bar; // initializer
+ ( id )fooWithBar:(NSInteger)bar; // convenience constructor
@end
这个问题可不只有学术意义,比如,[[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData]在Mac OS X(只在该OS版本)中会报错“Multiple methods named 'writeData:' found with mismatched result, parameter type or attributes.”原因是NSFileHandle和NSURLHandle都提供writeData:方法。由于[NSFileHandle fileHandleWithStandardOutput] 返回的类型是id,编译器并不确定请求了哪个类的writeData:方法。
你可以用
[(NSFileHandle*)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];
或
NSFileHandle*fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];
来绕过。
initializer的情况更复杂,当你输入
- ( id ) initWithBar: (NSInteger) bar编译器会假设你输入了
- (instancetype) initWithBar: (NSInteger) bar
对于ARC而言,这是必须的。 Clang Language Extensions 的相关结果类型(Related result types)也讲到了这一点。也许别人会据此告诉你不必使用instancetype,但我建议你用它。下面解释我为什么如此建议。
使用instancetype有三点好处:
1、明确性。代码只做你让它做的事,而不是其他。
2、程式化。你会养成好习惯,这些习惯在某些时候会很有用,而且肯定有用武之地。
3、一致性。让代码可读性更好。
明确性
这两行代码对于编译器来说是一样的:
- ( id ) initWithBar: (NSInteger) bar ;
- (instancetype) initWithBar: (NSInteger) bar ;
但在你眼里,这两行代码却不同。你不该学着忽视它。
模式化
在使用init等方法时的确没有区别,但在定义简易构造函数时就有区别了。
这两行代码并不等价:
+ ( id ) fooWithBar: (NSInteger) bar ;
+ (instancetype) fooWithBar: (NSInteger) bar ;
如果用instancetype作为函数的返回类型,就不会出错。
一致性:
最后,想象把所有东西放到一起时的情景:你想要一个init方法和一个简易构造函数。
如果你用id来作为init函数的返回类型,最终代码如下:
- ( id ) initWithBar: (NSInteger) bar ;
+ (instancetype) fooWithBar: (NSInteger) bar ;
但如果你用instancetype,代码如下:
- (instancetype) initWithBar: (NSInteger) bar ;
+ (instancetype) fooWithBar: (NSInteger) bar ;
代码更加一致,可读性更强。它们返回相同的东西,这一点一目了然。
除非你有意为旧编译器写代码,不然你在合适的时候都应该用instancetype。
在写一条返回id的消息前,问自己:这个类返回实例吗?如果返回,用instancetype。
肯定有需要返回id的时候,但你用instancetype的频率应该会更高。