instancetype
据我所知,Clang添加了一个关键字,据我所知,它替换id
为-alloc
and中的返回类型init
。
使用instancetype
代替有好处id
吗?
id
已被替换 instancetype
,甚至是init
从NSObject
。如果您想使代码与Swift兼容,则必须使用instancetype
instancetype
据我所知,Clang添加了一个关键字,据我所知,它替换id
为-alloc
and中的返回类型init
。
使用instancetype
代替有好处id
吗?
id
已被替换 instancetype
,甚至是init
从NSObject
。如果您想使代码与Swift兼容,则必须使用instancetype
Answers:
绝对有好处。当使用'id'时,实际上根本没有类型检查。使用instancetype,编译器和IDE知道将返回什么类型的东西,并且可以更好地检查代码并更好地自动完成。
仅在合理的情况下使用它(即返回该类实例的方法);id仍然有用。
alloc
,init
等instancetype
由编译器自动升级。但这并不是说没有好处。有,但是不是。
是的,instancetype
在所有适用的情况下使用都有好处。我将更详细地解释,但让我从以下粗体语句开始:instancetype
在适当时使用,即当类返回相同类的实例时使用。
实际上,这就是苹果现在在这个问题上所说的:
在您的代码中,将出现的
id
作为返回值替换为instancetype
适当的位置。init
方法和类工厂方法通常是这种情况。即使编译器自动转换以“ alloc”,“ init”或“ new”开头且返回类型id
为return的方法instancetype
,它也不会转换其他方法。Objective-C约定是instancetype
为所有方法显式编写。
顺便说一句,让我们继续并解释为什么这是一个好主意。
首先,一些定义:
@interface Foo:NSObject
- (id)initWithBar:(NSInteger)bar; // initializer
+ (id)fooWithBar:(NSInteger)bar; // class factory
@end
对于班级工厂,应始终使用instancetype
。编译器不会自动转换id
为instancetype
。那id
是一个通用对象。但是,如果您将其设为instancetype
,编译器就会知道该方法返回的对象类型。
这不是学术问题。例如,[[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData]
将在Mac OS X上生成错误(仅)。发现多个名为'writeData:'的方法,它们的结果,参数类型或属性不匹配。原因是NSFileHandle和NSURLHandle都提供了一个writeData:
。由于[NSFileHandle fileHandleWithStandardOutput]
返回id
,编译器不确定writeData:
正在调用什么类。
您需要使用以下任一方法来解决此问题:
[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];
要么:
NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];
当然,更好的解决方案是声明fileHandleWithStandardOutput
为返回instancetype
。这样就不需要强制转换或分配。
(请注意,在iOS上,此示例不会产生错误,因为它仅NSFileHandle
提供了a writeData:
。存在其他示例,例如length
,它返回CGFloat
from,UILayoutSupport
但是NSUInteger
from NSString
。)
注意:自从我撰写本文以来,macOS标头已被修改为返回a NSFileHandle
而不是an id
。
对于初始化程序而言,它更加复杂。当您键入以下内容时:
- (id)initWithBar:(NSInteger)bar
…编译器会假装您键入以下内容:
- (instancetype)initWithBar:(NSInteger)bar
这对于ARC是必需的。Clang语言扩展相关的结果类型对此进行了描述。这就是为什么人们会告诉您没有必要使用的原因instancetype
,尽管我认为您应该这样做。此答案的其余部分处理此问题。
有三个优点:
的确,从退货没有技术上的好处。但这是因为编译器会自动将转换为。您是在依赖这个怪癖;当您编写返回值an时,编译器会将其解释为好像返回了。instancetype
init
id
instancetype
init
id
instancetype
这些等效于编译器:
- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;
这些不等于你的眼睛。充其量,您将学会忽略差异并略过差异。这不是您应该学会忽略的东西。
虽然有没有区别init
等方法,也就是只要你定义一个类工厂的差异。
这两个不相等:
+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
您需要第二种形式。如果习惯于将类型instancetype
用作构造函数的返回类型,则每次都会正确使用它。
最后,想象一下是否将它们放在一起:既要有一个init
函数又要有一个类工厂。
如果将id
for用于init
,则最终会得到如下代码:
- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
但是,如果使用instancetype
,则会得到以下信息:
- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
它更一致,更易读。他们返回相同的东西,现在很明显。
除非有意为旧的编译器编写代码,否则应instancetype
在适当时使用。
在编写返回的消息之前,请先犹豫id
。问问自己:这是否返回该类的实例?如果是这样,则为instancetype
。
在某些情况下,您需要返回id
,但是您可能instancetype
会更频繁地使用它。
instancetype
vs id
真的不是风格决定。最近的变化instancetype
确实表明,我们应该instancetype
在诸如-init
“我的班级实例”之类的地方使用
以上答案足以解释这个问题。我只想添加一个示例,供读者理解编码方面的知识。
A类
@interface ClassA : NSObject
- (id)methodA;
- (instancetype)methodB;
@end
B级
@interface ClassB : NSObject
- (id)methodX;
@end
TestViewController.m
#import "ClassA.h"
#import "ClassB.h"
- (void)viewDidLoad {
[[[[ClassA alloc] init] methodA] methodX]; //This will NOT generate a compiler warning or error because the return type for methodA is id. Eventually this will generate exception at runtime
[[[[ClassA alloc] init] methodB] methodX]; //This will generate a compiler error saying "No visible @interface ClassA declares selector methodX" because the methodB returns instanceType i.e. the type of the receiver
}
您还可以在“指定的初始化程序”中获取详细信息
**
**此关键字只能用于返回类型,与接收者的返回类型匹配。init方法始终声明为返回instancetype。例如,为什么不将返回类型设为Party呢?如果党的阶级曾经被阶级化,那将引起一个问题。子类将继承Party的所有方法,包括初始化程序及其返回类型。如果子类的实例发送了此初始化消息,那会返回吗?不是指向Party实例的指针,而是指向子类实例的指针。您可能会认为这没问题,我将重写子类中的初始化程序以更改返回类型。但是在Objective-C中,不能有两个方法具有相同的选择器和不同的返回类型(或参数)。通过指定初始化方法返回“
**在Objective-C中引入实例类型之前,初始化程序将返回id(eye-dee)。此类型定义为“指向任何对象的指针”。(id很像C中的void *。)撰写本文时,XCode类模板仍然使用id作为添加在样板代码中的初始化程序的返回类型。与instancetype不同,id不仅可以用作返回类型,还可以用作ID。当您不确定变量最终指向哪种对象时,可以声明类型为id的变量或方法参数。在使用快速枚举迭代多个或未知类型的对象的数组时,可以使用id。请注意,由于id未定义为“指向任何对象的指针”,因此在声明此类型的变量或对象参数时,请勿包含*。