AnyObject如何遵守NSObjectProtocol?

How Does AnyObject Conform to NSObjectProtocol?

这个问题的灵感来自 on the question

考虑空 Swift class:

class MyClass { }

尝试在此 class 的实例上调用任何 NSObjectProtocol 方法将导致编译时错误:

let obj = MyClass()
obj.isKindOfClass(MyClass.self) // Error: Value of type 'MyClass' has no member 'isKindOfClass'

但是,如果我将实例转换为 AnyObject,我的对象现在符合 NSObjectProtocol,我可以调用协议定义的实例方法:

let obj: AnyObject = MyClass()
obj.isKindOfClass(MyClass.self) // true
obj.conformsToProtocol(NSObjectProtocol) // true
obj.isKindOfClass(NSObject.self) // false

我的对象没有继承自 NSObject,但仍然符合 NSObjectProtocolAnyObject如何符合NSObjectProtocol

在 Cocoa / Objective-C 世界中,AnyObject 是 id。将此对象转换为 AnyObject 后,您可以向其发送任何已知的 Objective-C 消息,例如 isKindOfClassconformsToProtocol。现在,当你说 isKindOfClassconformsToProtocol 时,你已经不在 Swift 世界中了;您正在用 Objective-C 与 Cocoa 交谈。所以想想Objective-C是怎么看这个对象的。 Objective-C 世界中的所有 classes 都来自某个基础 class;像 MyClass 这样毫无根据的 class 是不可能的。 Objective-C 世界中的每个基础 class 都符合 NSObject 协议(Swift 调用 NSObjectProtocol);这就是基础 class 的意义(或起源)!因此,为了让它进入 Objective-C 世界,Swift 将 MyClass 呈现为从一个特殊的桥接基础 class SwiftObject 下降,它确实符合 NSObjectProtocol (如您所见这里:https://github.com/apple/swift/blob/master/stdlib/public/runtime/SwiftObject.mm).

如果我根据 matt's 的回答正确理解了这一点,那么这在 Swift / Objective-C 互操作可用时有效,因为实际上 Swift class 类型最终继承自 SwiftObject,当 Objective-C 互操作被编译时,实际上涉及一个 Objective-C class (Swift 对象在 [=13= 中实现] 当使用 Objective-C 互操作时编译为 Objective-C++)。因此,将 Swift class 类型的对象转换为 AnyObject 类型的 "leaks" 该信息。

Swift source code、文件 swift/stdlib/public/runtime/SwiftObject.mm:

中查看实现中的一些相关位
#if SWIFT_OBJC_INTEROP

// …

@interface SwiftObject<NSObject> {
   SwiftObject_s header;
}

// …

@implementation SwiftObject

// …

- (BOOL)isKindOfClass:(Class)someClass {
  for (auto isa = _swift_getClassOfAllocated(self); isa != nullptr;
       isa = _swift_getSuperclass(isa))
    if (isa == (const ClassMetadata*) someClass)
      return YES;

  return NO;
}

// …

// #endif

据此预测,Swift 3 in Linux(其中没有 Objective-C 运行时作为 Swift 运行时和基础实现的一部分可用我明白了吗?)来自这个问题的示例代码和启发这个问题的 失败并出现以下错误编译器错误:

ERROR […] value of type 'AnyObject' has no member 'isKindOfClass'

除了matt's的回答,我认为是正确的:

Is isKindOfClass in this case actually sent as a dynamically dispatched message, even though the class itself is not an Objective-C visible type and does not use messaging based dispatch for its own methods?

不,isKindOfClass 作为动态调度方法发送 因为 class 本身 一个 Objective-C 可见类型并且 确实 使用基于消息传递的调度作为它自己的方法。

这样做是因为 @objc public protocol AnyObject {}

中的 @objc

如果您 cmd-click 在 XCode 中的 AnyObject 上,您将在生成的 headers

中看到它
/// When used as a concrete type, all known `@objc` methods and 
/// properties are available, as implicitly-unwrapped-optional methods 
/// and properties respectively, on each instance of `AnyObject`.

并且在 https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html

的文档中

To be accessible and usable in Objective-C, a Swift class must be a descendant of an Objective-C class or it must be marked @objc.

(我的重点)

采用标有 @objc 的协议意味着您的 class 是一个 @objc class 并且是通过 mz2 指出的互操作机制桥接的 ObjC在上面的答案中。

为已经很好的答案添加一些额外的信息。

我创建了三个程序并查看了每个程序生成的程序集:

obj1.swift

import Foundation
class MyClass { }
let obj = MyClass()

obj2.swift

import Foundation
class MyClass { }
let obj: AnyObject = MyClass()

obj3.swift

import Foundation
class MyClass { }
let obj: AnyObject = MyClass()
obj.isKindOfClass(MyClass.self)

obj1 和 obj2 之间的区别是微不足道的。任何涉及对象类型的指令都有不同的值:

movq    %rax, __Tv3obj3objCS_7MyClass(%rip)

# ...

globl   __Tv3obj3objCS_7MyClass         .globl  __Tv3obj3objPs9AnyObject_
.zerofill __DATA,__common,__Tv3obj3objCS_7MyClass,8,3

# ...

.no_dead_strip  __Tv3obj3objCS_7MyClass

movq    %rax, __Tv3obj3objPs9AnyObject_(%rip)

# ...

.globl  __Tv3obj3objPs9AnyObject_
.zerofill __DATA,__common,__Tv3obj3objPs9AnyObject_,8,3

# ...

.no_dead_strip  __Tv3obj3objPs9AnyObject_

完全差异 here.

这对我来说很有趣。如果两个文件之间的唯一区别是对象类型的名称,为什么声明为 AnyObject 的对象可以执行 Objective-C 选择器?

obj3 展示了 isKindOfClass: 选择器是如何触发的:

LBB0_2:
    # ...
    movq    __Tv3obj3objPs9AnyObject_(%rip), %rax
    movq    %rax, -32(%rbp)
    callq   _swift_getObjectType
    movq    %rax, -8(%rbp)
    movq    -32(%rbp), %rdi
    callq   _swift_unknownRetain
    movq    -24(%rbp), %rax
    cmpq    , (%rax)
    movq    %rax, -40(%rbp)
    jne LBB0_4
    movq    -24(%rbp), %rax
    movq    8(%rax), %rcx
    movq    %rcx, -40(%rbp)
LBB0_4:
    movq    -40(%rbp), %rax
    movq    "L_selector(isKindOfClass:)"(%rip), %rsi
    movq    -32(%rbp), %rcx
    movq    %rcx, %rdi
    movq    %rax, %rdx
    callq   _objc_msgSend
    movzbl  %al, %edi
    callq   __TF10ObjectiveC22_convertObjCBoolToBoolFVS_8ObjCBoolSb
    movq    -32(%rbp), %rdi
    movb    %al, -41(%rbp)
    callq   _swift_unknownRelease
    xorl    %eax, %eax
    addq    , %rsp

# ...

LBB6_3:
    .section    __TEXT,__objc_methname,cstring_literals
"L_selector_data(isKindOfClass:)":
    .asciz  "isKindOfClass:"

    .section    __DATA,__objc_selrefs,literal_pointers,no_dead_strip
    .align  3
"L_selector(isKindOfClass:)":
    .quad   "L_selector_data(isKindOfClass:)"

obj2 和 obj3 之间的差异 here

isKindOfClass 作为动态调度方法发送,如 _objc_msgSend 所示。两个对象都暴露给Objective-C为SwiftObject.quad _OBJC_METACLASS_$_SwiftObject),声明对象的类型为AnyObject完成到NSObjectProtocol.[=26=的桥接]