在Objective C中,我为什么允许指定一个NSArray到NSMutableArray没有错误或警告? [英] In Objective C, why am I allowed to assign an NSArray to an NSMutableArray without error or warning?

查看:198
本文介绍了在Objective C中,我为什么允许指定一个NSArray到NSMutableArray没有错误或警告?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我被一个奇怪的行为所困扰,如下例所示:

  NSMutableArray * a1 = [[NSMutableArray alloc ] 在里面]; // fine 
NSMutableArray * a2 = [NSMutableArray array]; // fine,too

//编译器报告不兼容的指针类型; good:
NSMutableArray * a3 = [[NSArray alloc] init];

//编译器没有说什么,很高兴分配这个!
NSMutableArray * a4 = [NSArray array];

两个 init NSArray NSMutableArray 类的数组方法返回 id 。然而,当我调用这些方法的行为是不一样的,clang让我很高兴地分配一个空 NSArray NSMutableArray variable!



事实证明, clang会自动改变一些方法的返回类型,包括 init family, instancetype ,因此能够在编译时确定 [[NSArray alloc] init] 返回一个 NSArray * 不是 NSMutableArray * 。但是这个检查根本不适用于数组方法。



为什么?应该不是像我的最后一个例子生成至少一个警告?为什么不是所有这些方法都声明为返回 instancetype



好消息:从iOS 7开始, [NSArray array] 返回 instancetype 赋值到 a4 上面也会产生警告。 arrayWithContentsOfFile: arrayWithContentsOfURL 的其他方法仍然返回 id 虽然...

解决方案


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


由于您链接的文档描述,因为 -array 不会生成可识别的相关结果类型。 ObjC非常动态 - 编译器不能保证 +数组的结果类型。它使用一些方法做这个假设,因为命名约定被很好地定义(例如 + alloc -init code> + new -self 等)。



编译器还会验证您不希望看到的区域中的一些命名约定:

  @implementation NSArray(DEMO)

- (id)initStr
{
return [NSString new]; //<<警告。 RE:init prefix
}

@end




不应该像我的最后一个例子行生成至少一个警告?为什么不将所有这些方法声明为返回instancetype?将来会改变吗?


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



这样的文件错误,给工具和库几年更新。假设进行了更改,它可能会在主要的操作系统更新发生。



这可能是最好的,如果它被启用作为可选警告一段时间系统头的情况)。当然,他们仍然可以使用它与旧的编译器的新的API的向后兼容性。



此外,这种变化可以很容易地改进(不是更早的编译器会有意义通过简单的typedef的 id instancetype 之间的语义差异。 typedef的一个问题是它是一个全局声明 - 编译器可以将单词/修饰符/属性限制到给定范围,而不会通过添加全局typedef来模拟关键字的所有痛苦。苹果的GCC可能永远不支持 instancetype ,所以引入它的苹果的GCC的逻辑方式可能是一个全局 typedef code> id ,这可能会导致一些人的问题(没有语义的好处,如果路由被采取)。注意,苹果在过去也做过类似的突破性改变。


I'm disturbed by a weird behavior, illustrated by the following example:

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]; 

Both init and array method of both the NSArray and NSMutableArray classes return id. However, the behavior when I call these methods is simply not the same, and clang lets me happily assign an empty NSArray to an NSMutableArray variable!

It turns out that clang will automatically change the return type of some methods, including the init family, to instancetype, and thus be able to determine at compile time that [[NSArray alloc] init] returns an NSArray * and not an NSMutableArray *. But this check simply doesn't work with the array method.

Why? Shouldn't lines like my last example generate at least a warning? Why aren't all these methods declared as returning instancetype? Will it change in the future?


Update

Good news: as of iOS 7, [NSArray array] returns instancetype, so the assignment to a4 above also yields a warning. Other methods like arrayWithContentsOfFile: or arrayWithContentsOfURL still return id, though…

解决方案

But this check simply doesn't work with the array method. Why?

As the document you have linked describes, it is because -array does not yield a recognized Related Result Type. ObjC is very dynamic -- the compiler cannot guarantee the result type of +array. It does make that assumption with some methods because the naming conventions are well defined (e.g. +alloc, -init, +new, -self, etc.). So this implementation simply resorts to naming conventions.

The compiler also validates some naming conventions in areas you may not expect:

@implementation NSArray (DEMO)

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

@end

Shouldn't lines like my last example generate at least a warning? Why aren't all these methods declared as returning instancetype? Will it change in the future?

instancetype was introduced about one year ago (from the looks of it). Some of the APIs were written decades ago. I suspect it will happen -- in time -- because (if used correctly) it can point out a lot of issues in existing code. Of course, those changes would break existing builds (again, typically good corrections if declared in the right places).

So file bugs and give the tools and libraries a few years to update. Assuming the changes are made, it will probably happen at a major OS update.

It would probably be best if it were enabled as an optional warning for some time (in the case of the system headers). Of course, they could still employ it with backwards compatibility for older compilers for new APIs.

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.

这篇关于在Objective C中,我为什么允许指定一个NSArray到NSMutableArray没有错误或警告?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆