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
,但仍然符合 NSObjectProtocol
。 AnyObject
如何符合NSObjectProtocol
?
在 Cocoa / Objective-C 世界中,AnyObject 是 id
。将此对象转换为 AnyObject 后,您可以向其发送任何已知的 Objective-C 消息,例如 isKindOfClass
或 conformsToProtocol
。现在,当你说 isKindOfClass
或 conformsToProtocol
时,你已经不在 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`.
的文档中
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=的桥接]
这个问题的灵感来自
考虑空 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
,但仍然符合 NSObjectProtocol
。 AnyObject
如何符合NSObjectProtocol
?
在 Cocoa / Objective-C 世界中,AnyObject 是 id
。将此对象转换为 AnyObject 后,您可以向其发送任何已知的 Objective-C 消息,例如 isKindOfClass
或 conformsToProtocol
。现在,当你说 isKindOfClass
或 conformsToProtocol
时,你已经不在 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`.
的文档中
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=的桥接]