objc_copyClassList 甚至无法枚举了?

objc_copyClassList not even enumerable anymore?

这是 and 的导数。

在 iOS 15,如果我只是尝试获取并枚举 objc 运行时的 类,等待 viewDidAppear 以确保没有初始化问题:

var count = UInt32(0)
var classList = objc_copyClassList(&count)!
print("COUNT \(count)")
print("CLASS LIST \(classList)")
for i in 0..<Int(count) {
    print("\(i)")
    classList[i]
}

Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1820e0cdc)

之前生成以下内容
COUNT 28353
CLASS LIST 0x000000010bf24000
0
1
2
2022-02-17 16:24:02.977904-0800 TWiG V[2504:705046] *** NSForwarding: warning: object 0x1dbd32148 of class '__NSGenericDeallocHandler' does not implement methodSignatureForSelector: -- trouble ahead
2022-02-17 16:24:02.978001-0800 TWiG V[2504:705046] *** NSForwarding: warning: object 0x1dbd32148 of class '__NSGenericDeallocHandler' does not implement doesNotRecognizeSelector: -- abort

除了获取值外,我不知道该怎么做。我不想打印它或任何东西,但它仍然失败。有没有我缺少的魔法?

(我没有开启强化运行时,XCode 13.2.1)

我确实在 Apple 论坛上得到了这个问题的答案。以下代码有效:

var count: UInt32 = 0
let classList = objc_copyClassList(&count)!
defer { free(UnsafeMutableRawPointer(classList)) }
let classes = UnsafeBufferPointer(start: classList, count: Int(count))
for cls in classes {
    print(String(cString: class_getName(cls)))
}

我很惊讶为什么会这样,但我的却不行。 Here是相关解释:

I avoided this line in your code:

classList[i]

That line trigger’s Swift’s dynamic cast infrastructure, which relies on -methodSignatureForSelector:, which isn’t implemented by the __NSGenericDeallocHandler class.

So, the special sauce is this:

if you’re working with an arbitrary class you discover via the Objective-C runtime, you have to be very careful what you do with it. I recommend that you use Objective-C runtime calls to interrogate the class to make sure it behaves reasonably before you let it ‘escape’ into code that you don’t control, like the Swift runtime.

In my example I used the Objective-C runtime routine class_getName to get the class name, but there are a bunch of other things that you can do to identify that you’re working with a reasonable class before you let it escape into general-purpose code, like the Swift runtime.