This is similar to this post https://developer.apple.com/forums/thread/700770 on using objc_copyClassList
to obtain the available classes. When iterating the list, I try casting the result to an instance of a protocol and that works fine:
protocol DynamicCounter {
init(controlledByPlayer: Bool, game: Game)
}
class BaseCounter: NSObject, DynamicCounter {
}
static func withAllClasses<R>(
_ body: (UnsafeBufferPointer<AnyClass>) throws -> R
) rethrows -> R {
var count: UInt32 = 0
let classListPtr = objc_copyClassList(&count)
defer {
free(UnsafeMutableRawPointer(classListPtr))
}
let classListBuffer = UnsafeBufferPointer(
start: classListPtr, count: Int(count)
)
return try body(classListBuffer)
}
static func initialize() {
let monoClasses = withAllClasses { $0.compactMap { $0 as? DynamicCounter.Type } }
for cl in monoClasses {
cl.initialize()
}
}
The above code works fine if I use DynamicCounter.Type
on the cast but crashes if try casting to BaseCounter.Type
instead.
Is there a way to avoid the weird and non Swift classes?
Perfect! Thanks.
So I’m testing this in a command-line tool rather than the REPL. My experience is that, when dealing with odd stuff, it’s best to avoid both the REPL and playgrounds.
Oh, and I’m testing an macOS 14.7, although I don’t expect the exact macOS version to matter.
In my test setup I was able to get your code working with this change:
static func initialize() {
let monoClasses = withAllClasses { $0.compactMap { thisClass -> BaseCounter.Type? in
guard class_getSuperclass(thisClass) != nil else {
print("no super, class: \(thisClass)")
return nil
}
return thisClass as? BaseCounter.Type
} }
for cl in monoClasses {
let o = cl.init(false)
print(o)
}
}
In short, I pre-filter out all base classes, that is, classes with no super classes. The end result is this:
no super, class: Object
no super, class: __NSGenericDeallocHandler
no super, class: __NSAtom
no super, class: _NSZombie_
no super, class: __NSMessageBuilder
no super, class: NSProxy
no super, class: _TtCs12_SwiftObject
no super, class: NSObject
<xxst.BaseCounter: 0x6000010100d0>
<xxst.TestCounter: 0x6000010100d0>
As you can see, there’s a bunch of classes that get filtered out. These classes are all ‘weird’, and something about them is triggering the Swift runtime to trap. Filtering them out is fine, because BaseCounter
and its subclasses all have an clear superclass.
I think it’d be reasonable for you to file a bug against the Swift runtime suggesting that it do this filtering itself. Please post your bug number, just for the record.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"