AnyClass是NSObjectProtocol ......有时候?

时间:2017-04-07 14:26:20

标签: objective-c swift objective-c-runtime

this question后,我对所描述的行为非常好奇,我做了一些调查,让我感到非常困惑。

问题

NSObjectProtocol的返回外,检查NSClassFromString返回NSClassFromString("WKNSURLRequest")的返回值是否为真。对PureClassSwiftObject来说,所有结果都是真实的事实对我来说有点令人惊讶。

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)

我们检查的不是这些类的实例,而是返回NSClassFromString的AnyClass?。

AnyClass是AnyObject.Type的typedef。 为什么NSObjectProtocol?为什么不选择WkRequestClass

mecki said是真的,我们可以通过阅读webkit源代码来检查它:WKNSURLRequest继承自WKObject这是一个符合NSObjectProtocol的根类,但是WKObject符合扩展NSObject(协议)的WKObject(协议)。

@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))

在这个简单的游乐场中,我玩了不同的类类型,最后我尝试使用objective-c runtime来内省WKNSURLRequestWKObject

如果崩溃是由运行时错误引起的,我也期待内省部分发生崩溃,但没有。没问题。

这是输出:

**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))

我得到了一次崩溃,这很荒谬。

这是否证明了目标c运行时被破坏/没有正确处理这种情况?答案看起来并不容易(这就是为什么我发布这个问题)因为正如预期的那样,WKObject符合NSObjectProtocol,并且它是一个根类,因为它的superClass是nil。所有这些都是为这种反思而努力的。

还有待检查的是swift运行时。有什么方法可以检查吗?我有什么遗漏可以解释这次崩溃的事吗?我很想知道你对此的看法。

1 个答案:

答案 0 :(得分:1)

WKObject在实现NSObject protocol时实现WKObject protocol并且此协议继承自NSObject protocol,这是正确的。但这不起作用。

+isSubclassOfClass:+instancesRespondToSelector:等某些方法未在NSObject协议中声明,这些方法只是NSObject class的常规类方法,因此被所有子类继承NSObject的{​​{1}},但仅限于NSObject的子类。其他根类必须自己实现这些,如果他们希望NSObject兼容,NSObject协议不会强制他们这样做。

现在从单元测试类中查看此代码:

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的要求,但它没有实现{ {1}}或+instancesRespondToSelector:,它并不是必须的,所以这是完全可以的。它是一个根类,它不会从+isSubclassOfClass:继承,并且没有协议要求它实现任何这些。我上面的错误是称这些方法;在对象上调用不存在的方法是&#34;未定义的行为&#34;这允许运行时几乎任何东西:忽略调用,只记录错误,抛出异常,或者只是立即崩溃;由于NSObject是一个高度优化的功能(它没有安全检查,每次调用都太贵了),它只会崩溃。

但显然斯威夫特似乎并不关心。在处理Obj-C对象时,Swift似乎认为它总是可以调用objc_msgSend()及其任何子类实现的这些方法之一,即使没有协议会保证这一点。这就是为什么某些Swift代码构造会导致Objective-C根对象无法从NSObject继承而导致崩溃的原因。而这个假设完全错了。 Swift 绝不能调用root对象上的任何方法,因为它们不确定这些方法是否也已实现。因此,我在另一个问题上将此称为Swift-Objc-Bridge中的一个错误。

更新1:

Giuseppe Lanza问道:

  

为什么当我有一个纯粹的swift类时,我从字符串中获取类   然后我测试是NSObjectProtocol我得到了吗?

我个人认为这也是Swift Runtime中的一个错误。纯Swift类不符合NSObjectProtocol。实际上它甚至不能符合它,见下面的答案。

Giuseppe Lanza问道:

  

请注意,如果我创建了一个继承自的协议   NSObjectProtocol,然后我尝试使PureClass符合   协议编译器会抱怨PureClass不是   符合NSObjectProtocol

那是因为NSObject必须实现所有必需的PureClass方法以符合该协议;看到这个答案https://stackoverflow.com/a/24650406/15809

然而,它甚至无法满足该要求,因为NSObjectProtocol的一个要求是实现此方法

NSObjectProtocol

并且这对于纯粹的Swift类来说根本不可能,因为当你尝试这样做时,编译器会抱怨:

func `self`() -> Self

这是正确的,纯粹的Swift类不能用Obj-C表示,所以它不能返回所需的类型。

Swift文档还说:

  

请注意,@ objc协议只能由那些类使用   继承自Objective-C类或其他@objc类。

目前error: method cannot be an implementation of an @objc requirement because its result type cannot be represented in Objective-C 迫使你继承@objc,纯粹的Swift类没有。{/ p>