14

我对一种奇怪的行为感到不安,如下例所示:

NSMutableArray *a1 = [[NSMutableArray alloc] init]; // fine
NSMutableArray *a2 = [NSMutableArray array];        // fine, too

// compiler reports incompatible pointer types; good:
NSMutableArray *a3 = [[NSArray alloc] init]; 

// compiler says nothing and is happy to assign this!
NSMutableArray *a4 = [NSArray array]; 

和类的initarray方法都返回。但是,当我调用这些方法时的行为根本不一样,clang 让我愉快地为变量赋值!NSArrayNSMutableArrayidNSArrayNSMutableArray

事实证明,clang 会自动将某些方法(包括initfamily)的返回类型更改为instancetype,从而能够在编译时确定[[NSArray alloc] init]返回的是 anNSArray *而不是 an NSMutableArray *。但是此检查根本不适用于该array方法。

为什么?像我上一个示例这样的行不应该至少产生一个警告吗?为什么不将所有这些方法都声明为返回instancetype?未来会改变吗?


更新

好消息:从 iOS 7 开始,[NSArray array]返回instancetype,因此对上述的赋值a4也会产生警告。其他方法喜欢arrayWithContentsOfFile:arrayWithContentsOfURL仍然返回id,但是......</p>

4

2 回答 2

10

但是这个检查根本不适用于数组方法。为什么?

正如您链接的文档所描述的那样,这是因为-array没有产生公认的相关结果类型。ObjC 是非常动态的——编译器不能保证+array. 它确实对某些方法做出了这种假设,因为命名约定是明确定义的(例如+alloc, -init, +new,-self等)。所以这个实现只是诉诸命名约定。

编译器还会在您可能不期望的区域验证一些命名约定:

@implementation NSArray (DEMO)

- (id)initStr
{
    return [NSString new]; // << warning. RE: init prefix
}

@end

像我上一个示例这样的行不应该至少产生一个警告吗?为什么不是所有这些方法都声明为返回实例类型?未来会改变吗?

instancetype大约是一年前推出的(从外观上看)。一些 API 是几十年前编写的。我怀疑它会及时发生——因为(如果使用正确)它可以指出现有代码中的很多问题。当然,这些更改会破坏现有的构建(同样,如果在正确的位置声明,通常是很好的更正)。

所以提交错误并给工具和库几年的更新时间。假设进行了更改,它可能会在主要的操作系统更新时发生。

最好将它作为可选警告启用一段时间(在系统标头的情况下)。当然,他们仍然可以为新 API 的旧编译器向后兼容使用它。

Also, this change could be retrofitted quite easily (not that earlier compilers would make sense of the semantic difference between id and instancetype) by a simple typedef. One problem with a typedef is that it is a global declaration -- a compiler could restrict a word/modifier/attribute to a given scope, without causing all the pain of simulating a keyword by adding a global typedef. Apple's GCC may never support instancetype, so the logical way to introduce it for Apple's GCC may be a global typedef of id, which could cause problems for some people (with no semantic benefit, if that route were taken). Note that similar breaking changes have been made by Apple in the past.

于 2013-03-06T14:40:41.263 回答
3

事实证明,您不仅可以使用错误的数组类型,还可以使用任何带有返回id. 例如,编译时没有看到警告:

NSMutableArray *a4 = [NSDictionary dictionary]; 

这是用于选择退出类型安全的副作用id,并且正如您所注意到的,它应该被弃用并替换为 instancetype(以上述方式使用时会引发不兼容的类型警告)。

不幸的是,这不是一个错误。 instancetype作为一个相当新的关键字,它的采用还没有普及,在 Apple 的框架中开始使用它是一个大胆的举措。你永远不知道,下一个 SDK 总是有希望的!

于 2013-03-06T14:05:02.573 回答