`type(of:)` vs `object_getClass` difference?

So I'm currently experimenting with isa-swizzling to create a unique runtime subclass of an objective-c object and I realized that type(of:) yields a different result than object_getClass.

If I would print a string describing those types, they would both print the same name, however the difference can be clearly made visible using NSStringFromClass function.

Some pseudocode:

let object = ... // any obj-c type, let's pretend it's called "A"
isaSwizzle(object)
// object becomes `A_UUID`
type(of: object) == object_getClass(object) // false
NSStringFromClass(object_getClass(object)!) // A_UUID
NSStringFromClass(type(of: object)) // still A

What am I missing? Does type(of:) have a bug or is it acting by design?


Additional information after more testing:

Isa swizzling an instance of a plain NSObject results in a more expected way:

NSStringFromClass(object_getClass(nsObject)!) // "NSObject"
NSStringFromClass(type(of: nsObject)) // "NSObject"

isaSwizzle(nsObject) 

NSStringFromClass(object_getClass(nsObject)!) // "_MyObject_CBA33942-2139-43E4-82DF-3446FFDD4386"
NSStringFromClass(type(of: nsObject)) // "_MyMyObject_CBA33942-2139-43E4-82DF-3446FFDD4386"

Okay someone pointed out this change on Slack:

I guess it's by design then!

That's right. Our thinking was that this kind of dynamically-generated subclass is usually not meant to be a semantic part of the type system and should not be reflected in normal reflective queries. That may be excessively tailored to the uses of dynamic subclasses that are part of the Apple platform APIs, but, well, those are important uses to get right.

If this is really problematic, we could probably figure out some way to have the Objective-C runtime distinguish semantic vs. non-semantic subclasses. But that's an awful lot of work for a pretty corner-case and non-native area of functionality.

6 Likes