【发布时间】:2013-03-06 09:30:44
【问题描述】:
这里是基本的 Cocoa 新手问题……这似乎是 Cocoa 中非常常见的模式。为什么像[NSArray arrayWithObjects: ...]这样的初始化方法和自动释放对象创建方法返回id?为什么返回 NSArray *(或正在创建的实际类型)并不是更好?
【问题讨论】:
标签: objective-c cocoa
这里是基本的 Cocoa 新手问题……这似乎是 Cocoa 中非常常见的模式。为什么像[NSArray arrayWithObjects: ...]这样的初始化方法和自动释放对象创建方法返回id?为什么返回 NSArray *(或正在创建的实际类型)并不是更好?
【问题讨论】:
标签: objective-c cocoa
init 方法返回 id,因此它们可以返回与其超类相同的类。
Objective-C 集合类返回 id,因为它们可以存储任何类型的 Objective-C 对象。
【讨论】:
instancetype 而不是id?
instancetype 是 clang (clang.llvm.org/docs/LanguageExtensions.html#objc_instancetype) 引入的扩展,并且库在 clang 之前就已经存在,所以我猜 id 主要用于历史目的。
另一个原因是返回值的类型选择器匹配。如果[NSArray arrayWithObjects: ...] 返回一个NSArray(通过声明),那么NSMutableArray 需要用不同的返回类型重新声明arrayWithObjects:(否则在编写NSMutableArray * a = [NSMutableArray arrayWithArray:] 时会收到警告)。
即使你走这条路,当编译器尝试将选择器与类型/非类型匹配时,消息链也会很快导致歧义。所以......选择器的返回和参数类型在这种情况下应该匹配。因此,使用了返回类型id。
如果您尝试使用此类构造函数构建严格类型的 objc 层次结构并使用高警告级别,您将看到这种歧义是如何展开的。当然,NSArray/NSMutableArray 的例子足以说明问题。
【讨论】:
[NSMutableArray array] 显然返回 NSMutableArray。
id。和 trojanfoe 链接了 bbum 的一个很好的解释。当然,您可以随意尝试,但初始化程序和构造函数通常是单行的,因此您可以立即(重新)使用变量声明来引入类型。
NSMutableArray *a1 = [NSArray array]; [a1 addObject:@"wrong!"];,这不是很令人不安吗?
实际上它确实返回一个 NSArray/NSMutalbeArray/whatever。
只是id是init的通用返回值。所以返回值被声明为 id 但它是一个正在返回的 NSArray。
但坦率地说,对于你的“为什么”问题,我无法给你任何好的答案。每当我覆盖子类中的 init 方法时,如果类的名称是 ABC,我会让它们返回 ABC*。它工作正常。
【讨论】:
init和其他一些特殊方法没有问题,因为clang treats them specially and automatically inserts the instancetype return type。