AnyClass 是 NSObjectProtocol ......有时?
AnyClass is NSObjectProtocol... sometimes?
在 之后,我对所描述的行为非常好奇,我做了一些调查,这让我很困惑。
问题
检查 NSObjectProtocol
对于 NSClassFromString
的 return return 在任何情况下都是正确的,除了 NSClassFromString("WKNSURLRequest")
的 return . PureClass
和 SwiftObject
.
所有结果都是真的让我有点惊讶
import UIKit
import WebKit
import ObjectiveC
class Sigh: NSObject { }
class PureClass { }
let sighClass = NSClassFromString(NSStringFromClass(Sigh.self))!
let pureClass = NSClassFromString(NSStringFromClass(PureClass.self))!
let nsObject = NSClassFromString("NSObject")!
let wkRequestClass = NSClassFromString("WKNSURLRequest")!
let swiftObject = NSClassFromString("SwiftObject")!
print("\n*NSObjectProtocol CONFORMANCE*")
print("NSObject: ", nsObject is NSObjectProtocol)
//print("WkRequestClass: ", wkRequestClass is NSObjectProtocol)
print("WkRequestClass: This would crash")
print("SighClass: ", sighClass is NSObjectProtocol)
print("PureClass: ", pureClass is NSObjectProtocol)
print("SwiftObject: ", swiftObject is NSObjectProtocol)
我们检查的不是那些 classes 的实例,而是 NSClassFromString
的 return 是 AnyClass?。
AnyClass 是 AnyObject.Type
的类型定义。 为什么是NSObjectProtocol
?为什么不 WkRequestClass
?
什么 是正确的,我们可以通过阅读 webkit 源代码来检查它: WKNSURLRequest
继承自 WKObject
,它是根 class 但符合 NSObjectProtocol
,因为 WKObject
符合扩展 NSObject(protocol) 的 WKObject(protocol)。
@protocol WKObject <NSObject>
@property (readonly) API::Object& _apiObject;
@end
NS_ROOT_CLASS
@interface WKObject <WKObject>
- (NSObject *)_web_createTarget NS_RETURNS_RETAINED;
@end
来源:https://github.com/WebKit/webkit/blob/master/Source/WebKit2/Shared/Cocoa/WKObject.h
Mecki 对这种崩溃的最佳猜测是运行时错误,所以我试图以某种方式对其进行解释。这是我的游乐场:
//: Playground - noun: a place where people can play
import UIKit
import WebKit
import ObjectiveC
class Sigh: NSObject { }
class PureClass { }
let sighClass: AnyClass = NSClassFromString(NSStringFromClass(Sigh.self))!
let pureClass: AnyClass = NSClassFromString(NSStringFromClass(PureClass.self))!
let nsObject: AnyClass = NSClassFromString("NSObject")!
let wkRequestClass: AnyClass = NSClassFromString("WKNSURLRequest")!
let swiftObject: AnyClass = NSClassFromString("SwiftObject")!
print("\n*NSObjectProtocol CONFORMANCE*")
print("NSObject: ", nsObject is NSObjectProtocol)
//print("WkRequestClass: ", wkRequestClass is NSObjectProtocol)
print("WkRequestClass: This would crash")
print("SighClass: ", sighClass is NSObjectProtocol)
print("PureClass: ", pureClass is NSObjectProtocol)
print("SwiftObject: ", swiftObject is NSObjectProtocol)
print("\n*ANYCLASS PRINT*")
print("NSObject: ", nsObject)
print("WkRequestClass: ", wkRequestClass)
print("SighClass: ", sighClass)
print("PureClass: ", pureClass)
print("SwiftObject: ", swiftObject)
print("\n*TYPE PRINT*")
print("Type of NSObject: ", type(of: nsObject))
print("Type of WkRequestClass: ", type(of: wkRequestClass))
print("Type of SighClass: ", type(of: sighClass))
print("Type of PureClass: ", type(of: pureClass))
print("Type of SwiftObject: ", type(of: swiftObject))
print("\n*.SELF PRINT*")
print("NSObject.self: ", nsObject.self)
print("WkRequestClass.self: ", wkRequestClass.self)
print("SighClass.self: ", sighClass.self)
print("PureClass.self: ", pureClass.self)
print("SwiftObject.self: ", swiftObject.self)
print("\n*SUPERCLASS PRINT*")
print("NSObject superClass: ", nsObject.superclass() ?? "nil")
//print("WkRequestClass superClass: ", wkRequestClass.superclass())
print("WkRequestClass superClass: This would crash")
print("SighClass superClass: ", sighClass.superclass() ?? "nil")
print("PureClass superClass: ", pureClass.superclass() ?? "nil")
print("SwiftObject superClass: ", swiftObject.superclass() ?? "nil")
print("\n*INTROSPECTION*\n")
var count: UInt32 = 0
var protocols = class_copyProtocolList(wkRequestClass, &count);
for i: Int in 0..<Int(count) {
print("WkRequestClass implements", protocols![i]!)
}
print("WkRequestClass superClass is", class_getSuperclass(wkRequestClass))
print("Its super super class is", class_getSuperclass(class_getSuperclass(wkRequestClass)))
//Introspecting WKObject
protocols = class_copyProtocolList(class_getSuperclass(wkRequestClass), &count);
for i: Int in 0..<Int(count) {
print("WKObject implements", protocols![i]!)
}
print("WKObject conforms the NSObjectProtocol? ", class_conformsToProtocol(class_getSuperclass(wkRequestClass), NSObjectProtocol.self))
在这个简单的操场上,我玩了一些不同的 class 类型,最后我尝试使用 objective-c 运行时来反思 WKNSURLRequest
和 WKObject
。
如果崩溃是由于运行时错误造成的,我预计内省部分也会崩溃,但没有。完全没问题。
这是输出:
**NSObjectProtocol CONFORMANCE**
- NSObject: true
- WkRequestClass: This would crash
- SighClass: true
- PureClass: true
- SwiftObject: true
**ANYCLASS PRINT**
- NSObject: NSObject
- WkRequestClass: WKNSURLRequest
- SighClass: Sigh
- PureClass: PureClass
- SwiftObject: SwiftObject
**TYPE PRINT**
- Type of NSObject: NSObject.Type
- Type of WkRequestClass: WKNSURLRequest.Type
- Type of SighClass: Sigh.Type
- Type of PureClass: PureClass.Type
- Type of SwiftObject: SwiftObject.Type
**.SELF PRINT**
- NSObject.self: NSObject
- WkRequestClass.self: WKNSURLRequest
- SighClass.self: Sigh
- PureClass.self: PureClass
- SwiftObject.self: SwiftObject
**SUPERCLASS PRINT**
- NSObject superClass: nil
- WkRequestClass superClass: This would crash
- SighClass superClass: NSObject
- PureClass superClass: SwiftObject
- SwiftObject superClass: nil
**INTROSPECTION**
- WkRequestClass implements ``
- WkRequestClass superClass is WKObject
- Its super super class is nil
- WKObject implements ``
- WKObject conforms the NSObjectProtocol? true
有趣的事实,如果我这样做
wkRequestClass.isSubclass(of: class_getSuperclass(wkRequestClass))
我崩溃了,这很荒谬。
这是否证明 objective c 运行时 broken/doesn 无法正确处理这种情况?答案看起来并不容易(这就是我发布这个问题的原因),因为正如预期的那样,WKObject 符合 NSObjectProtocol,并且它是一个根 class 因为它的超类是 nil。都是为了这种反省。
剩下要检查的是 swift 运行时。有什么方法可以检查吗?有什么我错过的可以解释这次崩溃吗?我很想知道你对此的看法。
你是正确的,WKObject
实现了 NSObject protocol
,因为它实现了 WKObject protocol
,并且该协议继承自 NSObject protocol
。但这在这里不起作用。
+isSubclassOfClass:
或 +instancesRespondToSelector:
等某些方法未在 NSObject
协议中声明,这些只是 NSObject class
的普通 class 方法,因此由 NSObject
的所有子 class 继承,但仅由 NSObject
的子 class 继承。如果其他根 classes 想要 NSObject
兼容,他们必须自己实现这些,NSObject
协议不会强制他们这样做。
现在从单元测试中检查这段代码 class:
SEL issubclasssel = @selector(isSubclassOfClass:);
Protocol * nsobjp = @protocol(NSObject);
Class c1 = NSClassFromString(@"NSObject");
XCTAssert(c1);
XCTAssert([c1 conformsToProtocol:nsobjp]);
XCTAssert([c1 instancesRespondToSelector:issubclasssel]);
XCTAssert([c1 isSubclassOfClass:[NSObject class]]);
Class c2 = NSClassFromString(@"WKNSURLRequest");
XCTAssert(c2);
XCTAssert([c2 conformsToProtocol:nsobjp]); // Line 1
XCTAssert([c2 instancesRespondToSelector:issubclasssel]); // Line 2
XCTAssert([c2 isSubclassOfClass:[NSObject class]]); // Line 3
此代码在第 2 行崩溃:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x18)
如果我注释掉第 2 行,代码仍然会在第 3 行崩溃并出现完全相同的错误。请注意,这不是 Swift 代码,也不以任何方式与 Swift 相关,这是纯 Objective-C 代码。这只是错误的 Objective-C 代码,您将在下面看到。
所以 WKObject
确实实现了 +conformsToProtocol:
(第 1 行不会崩溃),它必须实现,因为这是 NSObject protocol
的要求,但它没有实现 +instancesRespondToSelector:
或 +isSubclassOfClass:
,它不必,所以这完全没问题。它是一个根 class,它不继承自 NSObject
并且没有协议要求它实现其中任何一个。上面调用这些方法是我的错误;在对象上调用不存在的方法是 "undefined behavior" ,这允许运行时几乎任何事情:忽略调用、只记录错误、抛出异常或立即崩溃;由于 objc_msgSend()
是一个高度优化的函数(它没有安全检查,每次调用都太昂贵),它会崩溃。
但显然 Swift 有时似乎并不关心。在处理 Obj-C 对象时,Swift 似乎假设它总是可以调用 NSObject
及其任何子 class 实现的这些方法之一,即使没有协议会承诺那。这就是为什么某些 Swift 代码构造会导致 Objective-C 未从 NSObject
继承的根对象崩溃。因为这个假设是完全错误的。 Swift 绝不能 在根对象上调用任何无法确定这些方法是否也已实现的方法。因此我在另一个问题中称这是 Swift-Objc-Bridge 中的错误。
更新 1:
Giuseppe Lanza 问:
Why then when I have a pure swift class, I get the class from string
and then I test is NSObjectProtocol I get true?
我个人认为这也是 Swift 运行时的一个错误。纯 Swift class 不符合 NSObjectProtocol。实际上它甚至不能符合它,请参阅下面的答案。
Giuseppe Lanza 问:
Please note that if I create a protocol that inherits from
NSObjectProtocol and then I try to make PureClass conformance to that
protocol the compiler will complain that PureClass is not
NSObjectProtocol compliant
那是因为 PureClass
必须实现所有必需的 NSObjectProtocol
方法以符合该协议;看到这个答案
然而,它甚至无法满足该要求,因为 NSObjectProtocol
的一个要求是实现此方法
func `self`() -> Self
这对于纯 Swift class 来说根本不可能,因为当您尝试这样做时,编译器会抱怨:
error: method cannot be an implementation of an @objc requirement
because its result type cannot be represented in Objective-C
这是正确的,一个纯粹的 Swift class 不能在 Obj-C 中表示,所以它不能 return 所需的类型。
Swift 文档还说:
Note that @objc protocols can be adopted only by classes that
inherit from Objective-C classes or other @objc classes.
目前 @objc
强制您从 NSObject
继承,而纯 Swift class 则不会。
在
问题
检查 NSObjectProtocol
对于 NSClassFromString
的 return return 在任何情况下都是正确的,除了 NSClassFromString("WKNSURLRequest")
的 return . PureClass
和 SwiftObject
.
import UIKit
import WebKit
import ObjectiveC
class Sigh: NSObject { }
class PureClass { }
let sighClass = NSClassFromString(NSStringFromClass(Sigh.self))!
let pureClass = NSClassFromString(NSStringFromClass(PureClass.self))!
let nsObject = NSClassFromString("NSObject")!
let wkRequestClass = NSClassFromString("WKNSURLRequest")!
let swiftObject = NSClassFromString("SwiftObject")!
print("\n*NSObjectProtocol CONFORMANCE*")
print("NSObject: ", nsObject is NSObjectProtocol)
//print("WkRequestClass: ", wkRequestClass is NSObjectProtocol)
print("WkRequestClass: This would crash")
print("SighClass: ", sighClass is NSObjectProtocol)
print("PureClass: ", pureClass is NSObjectProtocol)
print("SwiftObject: ", swiftObject is NSObjectProtocol)
我们检查的不是那些 classes 的实例,而是 NSClassFromString
的 return 是 AnyClass?。
AnyClass 是 AnyObject.Type
的类型定义。 为什么是NSObjectProtocol
?为什么不 WkRequestClass
?
什么 WKNSURLRequest
继承自 WKObject
,它是根 class 但符合 NSObjectProtocol
,因为 WKObject
符合扩展 NSObject(protocol) 的 WKObject(protocol)。
@protocol WKObject <NSObject>
@property (readonly) API::Object& _apiObject;
@end
NS_ROOT_CLASS
@interface WKObject <WKObject>
- (NSObject *)_web_createTarget NS_RETURNS_RETAINED;
@end
来源:https://github.com/WebKit/webkit/blob/master/Source/WebKit2/Shared/Cocoa/WKObject.h
Mecki 对这种崩溃的最佳猜测是运行时错误,所以我试图以某种方式对其进行解释。这是我的游乐场:
//: Playground - noun: a place where people can play
import UIKit
import WebKit
import ObjectiveC
class Sigh: NSObject { }
class PureClass { }
let sighClass: AnyClass = NSClassFromString(NSStringFromClass(Sigh.self))!
let pureClass: AnyClass = NSClassFromString(NSStringFromClass(PureClass.self))!
let nsObject: AnyClass = NSClassFromString("NSObject")!
let wkRequestClass: AnyClass = NSClassFromString("WKNSURLRequest")!
let swiftObject: AnyClass = NSClassFromString("SwiftObject")!
print("\n*NSObjectProtocol CONFORMANCE*")
print("NSObject: ", nsObject is NSObjectProtocol)
//print("WkRequestClass: ", wkRequestClass is NSObjectProtocol)
print("WkRequestClass: This would crash")
print("SighClass: ", sighClass is NSObjectProtocol)
print("PureClass: ", pureClass is NSObjectProtocol)
print("SwiftObject: ", swiftObject is NSObjectProtocol)
print("\n*ANYCLASS PRINT*")
print("NSObject: ", nsObject)
print("WkRequestClass: ", wkRequestClass)
print("SighClass: ", sighClass)
print("PureClass: ", pureClass)
print("SwiftObject: ", swiftObject)
print("\n*TYPE PRINT*")
print("Type of NSObject: ", type(of: nsObject))
print("Type of WkRequestClass: ", type(of: wkRequestClass))
print("Type of SighClass: ", type(of: sighClass))
print("Type of PureClass: ", type(of: pureClass))
print("Type of SwiftObject: ", type(of: swiftObject))
print("\n*.SELF PRINT*")
print("NSObject.self: ", nsObject.self)
print("WkRequestClass.self: ", wkRequestClass.self)
print("SighClass.self: ", sighClass.self)
print("PureClass.self: ", pureClass.self)
print("SwiftObject.self: ", swiftObject.self)
print("\n*SUPERCLASS PRINT*")
print("NSObject superClass: ", nsObject.superclass() ?? "nil")
//print("WkRequestClass superClass: ", wkRequestClass.superclass())
print("WkRequestClass superClass: This would crash")
print("SighClass superClass: ", sighClass.superclass() ?? "nil")
print("PureClass superClass: ", pureClass.superclass() ?? "nil")
print("SwiftObject superClass: ", swiftObject.superclass() ?? "nil")
print("\n*INTROSPECTION*\n")
var count: UInt32 = 0
var protocols = class_copyProtocolList(wkRequestClass, &count);
for i: Int in 0..<Int(count) {
print("WkRequestClass implements", protocols![i]!)
}
print("WkRequestClass superClass is", class_getSuperclass(wkRequestClass))
print("Its super super class is", class_getSuperclass(class_getSuperclass(wkRequestClass)))
//Introspecting WKObject
protocols = class_copyProtocolList(class_getSuperclass(wkRequestClass), &count);
for i: Int in 0..<Int(count) {
print("WKObject implements", protocols![i]!)
}
print("WKObject conforms the NSObjectProtocol? ", class_conformsToProtocol(class_getSuperclass(wkRequestClass), NSObjectProtocol.self))
在这个简单的操场上,我玩了一些不同的 class 类型,最后我尝试使用 objective-c 运行时来反思 WKNSURLRequest
和 WKObject
。
如果崩溃是由于运行时错误造成的,我预计内省部分也会崩溃,但没有。完全没问题。
这是输出:
**NSObjectProtocol CONFORMANCE** - NSObject: true - WkRequestClass: This would crash - SighClass: true - PureClass: true - SwiftObject: true **ANYCLASS PRINT** - NSObject: NSObject - WkRequestClass: WKNSURLRequest - SighClass: Sigh - PureClass: PureClass - SwiftObject: SwiftObject **TYPE PRINT** - Type of NSObject: NSObject.Type - Type of WkRequestClass: WKNSURLRequest.Type - Type of SighClass: Sigh.Type - Type of PureClass: PureClass.Type - Type of SwiftObject: SwiftObject.Type **.SELF PRINT** - NSObject.self: NSObject - WkRequestClass.self: WKNSURLRequest - SighClass.self: Sigh - PureClass.self: PureClass - SwiftObject.self: SwiftObject **SUPERCLASS PRINT** - NSObject superClass: nil - WkRequestClass superClass: This would crash - SighClass superClass: NSObject - PureClass superClass: SwiftObject - SwiftObject superClass: nil **INTROSPECTION** - WkRequestClass implements `` - WkRequestClass superClass is WKObject - Its super super class is nil - WKObject implements `` - WKObject conforms the NSObjectProtocol? true
有趣的事实,如果我这样做
wkRequestClass.isSubclass(of: class_getSuperclass(wkRequestClass))
我崩溃了,这很荒谬。
这是否证明 objective c 运行时 broken/doesn 无法正确处理这种情况?答案看起来并不容易(这就是我发布这个问题的原因),因为正如预期的那样,WKObject 符合 NSObjectProtocol,并且它是一个根 class 因为它的超类是 nil。都是为了这种反省。
剩下要检查的是 swift 运行时。有什么方法可以检查吗?有什么我错过的可以解释这次崩溃吗?我很想知道你对此的看法。
你是正确的,WKObject
实现了 NSObject protocol
,因为它实现了 WKObject protocol
,并且该协议继承自 NSObject protocol
。但这在这里不起作用。
+isSubclassOfClass:
或 +instancesRespondToSelector:
等某些方法未在 NSObject
协议中声明,这些只是 NSObject class
的普通 class 方法,因此由 NSObject
的所有子 class 继承,但仅由 NSObject
的子 class 继承。如果其他根 classes 想要 NSObject
兼容,他们必须自己实现这些,NSObject
协议不会强制他们这样做。
现在从单元测试中检查这段代码 class:
SEL issubclasssel = @selector(isSubclassOfClass:);
Protocol * nsobjp = @protocol(NSObject);
Class c1 = NSClassFromString(@"NSObject");
XCTAssert(c1);
XCTAssert([c1 conformsToProtocol:nsobjp]);
XCTAssert([c1 instancesRespondToSelector:issubclasssel]);
XCTAssert([c1 isSubclassOfClass:[NSObject class]]);
Class c2 = NSClassFromString(@"WKNSURLRequest");
XCTAssert(c2);
XCTAssert([c2 conformsToProtocol:nsobjp]); // Line 1
XCTAssert([c2 instancesRespondToSelector:issubclasssel]); // Line 2
XCTAssert([c2 isSubclassOfClass:[NSObject class]]); // Line 3
此代码在第 2 行崩溃:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x18)
如果我注释掉第 2 行,代码仍然会在第 3 行崩溃并出现完全相同的错误。请注意,这不是 Swift 代码,也不以任何方式与 Swift 相关,这是纯 Objective-C 代码。这只是错误的 Objective-C 代码,您将在下面看到。
所以 WKObject
确实实现了 +conformsToProtocol:
(第 1 行不会崩溃),它必须实现,因为这是 NSObject protocol
的要求,但它没有实现 +instancesRespondToSelector:
或 +isSubclassOfClass:
,它不必,所以这完全没问题。它是一个根 class,它不继承自 NSObject
并且没有协议要求它实现其中任何一个。上面调用这些方法是我的错误;在对象上调用不存在的方法是 "undefined behavior" ,这允许运行时几乎任何事情:忽略调用、只记录错误、抛出异常或立即崩溃;由于 objc_msgSend()
是一个高度优化的函数(它没有安全检查,每次调用都太昂贵),它会崩溃。
但显然 Swift 有时似乎并不关心。在处理 Obj-C 对象时,Swift 似乎假设它总是可以调用 NSObject
及其任何子 class 实现的这些方法之一,即使没有协议会承诺那。这就是为什么某些 Swift 代码构造会导致 Objective-C 未从 NSObject
继承的根对象崩溃。因为这个假设是完全错误的。 Swift 绝不能 在根对象上调用任何无法确定这些方法是否也已实现的方法。因此我在另一个问题中称这是 Swift-Objc-Bridge 中的错误。
更新 1:
Giuseppe Lanza 问:
Why then when I have a pure swift class, I get the class from string and then I test is NSObjectProtocol I get true?
我个人认为这也是 Swift 运行时的一个错误。纯 Swift class 不符合 NSObjectProtocol。实际上它甚至不能符合它,请参阅下面的答案。
Giuseppe Lanza 问:
Please note that if I create a protocol that inherits from NSObjectProtocol and then I try to make PureClass conformance to that protocol the compiler will complain that PureClass is not NSObjectProtocol compliant
那是因为 PureClass
必须实现所有必需的 NSObjectProtocol
方法以符合该协议;看到这个答案
然而,它甚至无法满足该要求,因为 NSObjectProtocol
的一个要求是实现此方法
func `self`() -> Self
这对于纯 Swift class 来说根本不可能,因为当您尝试这样做时,编译器会抱怨:
error: method cannot be an implementation of an @objc requirement
because its result type cannot be represented in Objective-C
这是正确的,一个纯粹的 Swift class 不能在 Obj-C 中表示,所以它不能 return 所需的类型。
Swift 文档还说:
Note that @objc protocols can be adopted only by classes that inherit from Objective-C classes or other @objc classes.
目前 @objc
强制您从 NSObject
继承,而纯 Swift class 则不会。