This is a derivative of this and this.
On iOS 15(.3.1), if I just try to fetch and enumerate the classes of the objc runtime, waiting until viewDidAppear() to make sure there wasn't some initialization issue:
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]
}
produces the following before a 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
I don't know how to do any less with it than just fetching the value. I'm not trying to print it or anything, and yet it still fails. Is there some magic I'm missing?
Why have the API if the results crash your program? If the issue is legit, it would be nice of the docs pointed out a workaround, or at least the proper way to cope with the result.
(I do not have hardened runtime turned on, XCode 13.2.1)
I see 3 differences
All of those are best practice but they’re not the special sauce that makes this work. Rather, 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.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"