"id" 类型如何在不强制转换的情况下理解方法的接收者?

How "id" type understands the receiver of method without casting?

将 master 合并到我的工作分支后,我在线上遇到了编译器错误,该错误没有被更改。错误看起来像

id test;
[test count];

Multiple methods named 'count' found with mismatched result.

乍一看很清楚,因为编译器不知道"test"变量是哪个具体类型。但是我不明白为什么它以前有效。

  1. 如果我创建一个新文件,这一行有效,假设这是一个 NSArray 的方法。为什么编译器在这种情况下不显示错误?

  2. 在显示错误信息的同时,显示了几种可能的计数方法接收者。 (NSArray, NSDictionary, NSSet) 它是否搜索所有可以接收该消息的 类 并在有多个时显示错误?

  3. 我注意到导入“-Swift.h”文件时发生错误。这取决于什么?

编译器不会转换或检查您的 id 类型。它只是为您提供所有可能的 select 或。你说这个问题与导入“-Swift.h”文件有关。在这种情况下,请检查您的 Swift 代码,可能您有 Objective C 可见的 count 函数,其中 return 不同于 Int

此外,您可以检查 Issue navigator 中的问题,select 它会显示 Objective C 中可见的所有 count 个调用。全部检查,大部分都是return NSUInteger,但是应该有一个是return其他的,例如:

SWIFT_CLASS("_TtC3dev19YourClass")
@interface YourClass : NSObject
- (int32_t)count SWIFT_WARN_UNUSED_RESULT;
@end

Objective-C 不需要知道接收器的类型。在 run-time 处,所有 object 都只是 id,一切都是动态调度的。因此,任何消息都可以发送到任何 object,无论其类型如何。 (在 run-time,object 可以自由决定如何处理他们不理解的消息。最常见的事情是引发异常和崩溃,但有很多种 objects 可以处理不直接映射到方法调用的任意消息。)

然而,有几个技术细节使这个问题复杂化。

ABI(应用程序二进制接口)为 return 某些原始类型定义了不同的机制。只要值是 "a word-sized integer," 就没关系(这包括 NSInteger 和所有指针,这意味着所有 object 的扩展)。但是在某些处理器上,浮点数在与整数不同的寄存器中被 returned,并且结构(如 CGRect)可能会根据其大小以多种方式被 returned。为了编写必要的汇编语言,编译器必须知道它将是哪种 return 值。

ARC 添加了额外的皱纹,要求编译器更多地了解参数的类型(特别是它们是 objects 还是原语),以及是否有任何 memory-management必须考虑的属性。

编译器并不关心"real"类型test是什么,只要它能弄清楚-count的类型和属性即可。因此,在处理 id 值时,它会查看它可以看到的每个已知选择器(即在包含的 header 或当前 .m 中定义的每个选择器)。如果他们中有很多人在不同的 类 上也没关系,只要他们都同意。但是如果它根本找不到选择器,或者如果某些接口不一致,那么它就无法编译该行代码。

正如 lobstah 指出的那样,您的 Swift 代码中的某处可能有一个类型,它有一个名为 count()@objc 属性 的 @objc 方法名为 count 的 return 不是 Int(它映射到 NSInteger,因此与 -count 的通常签名匹配)。您需要修复该方法,或者您需要从 ObjC 中隐藏它(例如,通过添加 @nonobjc)。

或者更好:摆脱 id,并使用它的实际类型。 id 在 Cocoa 中通常是一个坏主意,如果您在其上调用方法则尤其是一个坏主意,因为编译器无法检查 object 是否会响应并且您可能会崩溃。