Nat! bio photo

Nat!

Senior Mull

Twitter Github Twitch

Should I fix fundamental bugs in Foundation or not ?

I recently made a significant change in the mulle-objc runtime. I typed the two different class types in Objective-C. In conventional (Apple) Objective-C lingo you have the class and the meta-class, both are of type struct objc_class. In mulle-objc there is now a struct _mulle_objc_infraclass and a struct _mulle_objc_metaclass, which are both based on struct _mulle_objc_class but they are significantly different.

One of the benefits of this change is, that at certain points in the code, I don’t have to wonder, if I need to deal with a metaclass. This can make some code faster. Also it made everything quite a bit more transparent, thus this article.

What is Class ?

  1. For the compiler it’s an opaque pointer type.
  2. For the Apple runtime progammer it’s a typedef for struct objc_class *.
  3. For the Foundation programmer Class is the front to the infraclass.

Unless the Foundation programmer is using direct access to the isa pointer, he can’t get access to the metaclass using -class or NSClassFromString or any other Foundation method or function.

Some tests with Apple Foundation

Clearly this is also the intention of the Apple Foundation, as -class will not return isa but always the infraclass.

[obj class] = 0x7fff7b2610f0
[[obj class] class] = 0x7fff7b2610f0
[NSObject class] = 0x7fff7b2610f0
[[NSObject class] class] = 0x7fff7b2610f0

Class isn’t defined in the mulle-objc runtime, but in the Foundation (MulleObjC). Having established that Class is always the infraclass, I changed the Class typedef tostruct _mulle_objc_infraclass *. And adapted some NSObject methods to the new, clearer vision.

But then tests started to fail…

Apples -isMemberOfClass is buggy, so MulleObjC is too

The actual implementation of -isMemberOfClass: in MulleObjC is surprisingly not:

- (BOOL) isMemberOfClass:(Class)
{
   return( [self class] == otherClass);
}

but to stay compatible with the Apple Foundation:

-(BOOL) isMemberOfClass:(Class)
{
   return( _mulle_objc_object_get_isa( self) == _mulle_objc_infraclass_as_class( otherClass));
}

It’s wrong because _mulle_objc_object_get_isa could return a metaclass, if self is a Class. Indeed this produces the unwanted result, that [[NSObject class] isMemberOfClass:[NSObject class]] will return NO.

What now ?

So why don’t I fix it ? Good question! What is better ? Be correct or stay compatible ? isKindOfClass: has the same problem.


Test code:

#ifndef __MULLE_OBJC__
# import <Foundation/Foundation.h>
#else
# import <MulleObjC/MulleObjC.h>
#endif


int   main( int argc, char *argv[])
{
   NSObject  *obj;

   obj = [NSObject new];

   printf( "[obj class] = %p\n", [obj class]);
   printf( "[[obj class] class] = %p\n", [[obj class] class]);
   printf( "[NSObject class] = %p\n", [NSObject class]);
   printf( "[[NSObject class] class] = %p\n", [[NSObject class] class]);
   printf( "[[NSObject class] isMemberOfClass:[NSObject class]] = %s\n",
   [[NSObject class] isMemberOfClass:[NSObject class]] ? "YES" : "NO");
   return( 0);
}

Compile with clang -o test test.m -framework Foundation.


Post a comment

All comments are held for moderation; basic HTML formatting accepted.

Name:
E-mail: (not published)
Website: