这个问题的灵感来自mz2's answer问题的Check for object type fails with "is not a type" error。
考虑一个空的Swift类:
class MyClass { }
尝试在此类的实例上调用任何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
?
答案 0 :(得分:5)
在Cocoa / Objective-C世界中,AnyObject是id
。将此对象强制转换为AnyObject后,您可以向其发送任何已知的Objective-C消息,例如isKindOfClass
或conformsToProtocol
。现在,当你说isKindOfClass
或conformsToProtocol
时,你不再是斯威夫特世界了;你正在和Objective-C谈论Cocoa。那么想想Objective-C如何看待这个对象。 Objective-C世界中的所有类都来自某个基类;像MyClass这样没有基础的类是不可能的。 Objective-C世界中的每个基类都符合NSObject协议(Swift称之为NSObjectProtocol);这是基类的原因(或从基类中下降)!因此,为了使它进入Objective-C世界,Swift将MyClass呈现为一个特殊的桥接基类SwiftObject,它确实符合NSObjectProtocol(正如你在这里看到的那样:https://github.com/apple/swift/blob/master/stdlib/public/runtime/SwiftObject.mm)。
答案 1 :(得分:5)
如果我根据JQuery documentation回答正确理解这一点,那么当Swift / Objective-C互操作可用时,这是有效的,因为实际上Swift类类型最终继承自java.io.FileNotFoundException: /storage/1B0C-330F/monfichier.txt: open failed: EACCES (Permission denied)
,当时目标 - 编译C interop,实际上涉及一个Objective-C类(SwiftObject在SwiftObject
中实现,当使用Objective-C interop时编译为Objective-C ++)。因此,将Swift类类型的对象转换为AnyObject类型的"泄漏"那个信息。
从matt's文件SwiftObject.mm
查看实施中的某些相关位:
swift/stdlib/public/runtime/SwiftObject.mm
正如预测的那样,在Linux中使用Swift 3(根据我的理解,在Swift运行时和基础实现中没有Objective-C运行时可用吗?)来自这个问题的示例代码和启发此问题的Swift source code因以下错误编译错误而失败:
#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
答案 2 :(得分:2)
在已经很好的答案中添加一些额外的信息。
我创建了三个程序,并查看了每个程序生成的程序集:
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
VS
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 $14, (%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 $48, %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
所示。这两个对象都以SwiftObject
(.quad _OBJC_METACLASS_$_SwiftObject
)的形式向Objective-C公开,声明对象的类型,AnyObject
完成了NSObjectProtocol
的桥接。
答案 3 :(得分:1)
除了matt's回答,我认为这是正确的:
在这种情况下,isKindOfClass实际上是作为动态调度的消息发送的,即使该类本身不是Objective-C可见类型,并且不为其自己的方法使用基于消息传递的调度吗?
不,isKindOfClass
是作为动态调度方法发送的,因为类本身 是Objective-C可见类型而是 >使用基于消息传递的调度来实现它自己的方法。
这是因为@objc
@objc public protocol AnyObject {}
如果你在XCode中点击AnyObject,你会在生成的标题中看到这个
/// 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`.
的文档中
要在Objective-C中可访问和使用,Swift类必须是Objective-C类或的后代,必须标记为@objc。
(我的重点)
采用标有@objc
的协议意味着您的类是@objc
类,并且是通过上面答案中mz2指出的互操作机制桥接的ObjC。