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
?
- For the compiler it’s an opaque pointer type.
- For the Apple runtime progammer it’s a typedef for
struct objc_class *
. - 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.