gpt4 book ai didi

objective-c - AnyClass 是 NSObjectProtocol ......有时?

转载 作者:行者123 更新时间:2023-11-28 06:21:35 32 4
gpt4 key购买 nike

正在关注 this question我对所描述的行为非常好奇,并进行了一些调查,这让我很困惑。

问题

检查 NSObjectProtocol 是否返回 NSClassFromString 在任何情况下都返回 true,除了 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 的类型定义。 为什么是NSObjectProtocol?为什么不是 WkRequestClass

什么 mecki said是真的,我们可以通过阅读 webkit 源代码来检查它:WKNSURLRequest 继承自 WKObject,它是一个根类,但符合 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 :

//: 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))

在这个简单的 Playground 上,我尝试了一些不同的类类型,最后我尝试使用 objective-c 运行时自省(introspection) WKNSURLRequestWKObject

如果崩溃是由于运行时错误引起的,我预计内省(introspection)部分也会崩溃,但没有。完全没有问题。

这是输出:

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

Funny fact, if I do

wkRequestClass.isSubclass(of: class_getSuperclass(wkRequestClass))

我崩溃了,这很荒谬。

这是否证明 objective-c 运行时已损坏/无法正确处理这种情况?答案看起来并不容易(这就是我发布这个问题的原因),因为正如预期的那样,WKObject 符合 NSObjectProtocol,并且它是一个根类,因为它的父类(super class)是 nil。都为这种反省而努力。

剩下要检查的是 swift 运行时。有什么方法可以检查吗?有什么我错过的可以解释这次崩溃吗?我很想知道您对此的看法。

最佳答案

你是对的 WKObject实现 NSObject protocol因为它实现了 WKObject protocol这个协议(protocol)继承自 NSObject protocol .但这在这里不起作用。

某些方法,如 +isSubclassOfClass:+instancesRespondToSelector:未在 NSObject 中声明协议(protocol),这些只是 NSObject class 的普通类方法并因此被 NSObject 的所有子类继承但仅限于 NSObject 的子类.其他根类如果想成为 NSObject 就必须自己实现这些。兼容,NSObject协议(protocol)不会强制他们这样做。

现在从单元测试类中检查这段代码:

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: ,它不必,所以这完全没问题。它是一个根类,它不继承自 NSObject并且没有协议(protocol)要求它实现其中任何一个。上面调用这些方法是我的错误;在对象上调用不存在的方法是“未定义的行为”,这允许运行时几乎任何事情发生:忽略调用、仅记录错误、抛出异常或立即崩溃;和 objc_msgSend()是一个高度优化的函数(它没有安全检查,每次调用都太昂贵),它就会崩溃。

但显然 Swift 有时似乎并不关心。在处理 Obj-C 对象时,Swift 似乎假设它总是可以调用这些方法之一 NSObject及其实现的任何子类,即使没有协议(protocol)会 promise 这一点。这就是为什么某些 Swift 代码构造会导致不继承自 NSObject 的 Objective-C 根对象崩溃的原因。 .因为这个假设是完全错误的。 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 Runtime 的一个 bug。纯 Swift 类不符合 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符合该协议(protocol)的方法;看到这个答案https://stackoverflow.com/a/24650406/15809

然而,作为NSObjectProtocol的一个要求,它甚至不能满足那个要求。就是实现这个方法

func `self`() -> Self

这对于纯 Swift 类来说根本不可能,因为当您尝试这样做时,编译器会报错:

error: method cannot be an implementation of an @objc requirement
because its result type cannot be represented in Objective-C

这是正确的,一个纯 Swift 类不能在 Obj-C 中表示,因此它不能返回所需的类型。

Swift 文档还说:

Note that @objc protocols can be adopted only by classes that inherit from Objective-C classes or other @objc classes.

目前@objc强制您继承 NSObject ,纯 Swift 类不会。

关于objective-c - AnyClass 是 NSObjectProtocol ......有时?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43280615/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com