Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Testing Class Equality" in Objective-c

There are situations in which people add new classes at runtime. One example is Key Value Observing: when you observe an object the Foundation framework creates a new subclass of the observed object's class. This dynamic class behaves in the same way as its superclass, but adds KVO notifications to all of its mutator methods.

The passage you quoted says that the Objective-C runtime can tell this new class apart from the original class. However, because it's just an implementation detail of the way KVO is built, you shouldn't know or care about it. The developers therefore overrode the -class method of their new class, to pretend that objects were still members of the original class.

If you want to check whether two objects are of the same class, you must therefore compare the results of their -class methods (which take tricks like KVO into account), instead of using runtime functions.

Here's an example:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSObject *observer = [NSObject new];
        NSObject *model = [NSObject new];

        [model addObserver: observer forKeyPath: @"count" options: 0 context: NULL];

        //using -class methods:
        NSLog(@"model is a %@, observer is a %@", [model class], [observer class]);

        //casting to Class:
        NSLog(@"model is a %@, observer is a %@", *(Class*)model, *(Class*)observer);

        //using the runtime:
        NSLog(@"model is a %@, observer is a %@", object_getClass(model), object_getClass(observer));

        [model removeObserver: observer forKeyPath: @"count" context: NULL];
        [model release];
        [observer release];
    }
    return 0;
}

You see that all I'm doing is creating two objects, telling one of them to observe the other, then finding out what their classes are. Here are the results:

2012-06-08 08:37:26.904 Untitled 2[896:707] model is a NSObject, observer is a NSObject

2012-06-08 08:37:26.907 Untitled 2[896:707] model is a NSKVONotifying_NSObject, observer is a NSObject

2012-06-08 08:37:26.907 Untitled 2[896:707] model is a NSKVONotifying_NSObject, observer is a NSObject

So as the documentation suggests, it's only the first case (where we compare -class) that does anything the application code could reasonably expect. The other two ways of finding out the class - asking the runtime, and casting the object pointer to a Class * - both give away implementation details about how KVO has changed the class from underneath us, and mean that the class comparison now won't show that the classes are equal.

Because other answers and comments are referring to -isMemberOfClass: and -isKindOfClass:, I'll cover those points too:

  • -isKindOfClass: is not a test for class equality. [object isKindOfClass: aClass] is true if object is an instance of aClass or any of its subclasses. Because the passage you've quoted is about class equality, -isKindOfClass: is not relevant here. That said, it's most often the test that you want to be doing in application code. It's more common to care about the answer to "can I use this object as a Foo?" than "is this object an instance exactly of Foo?".

  • -isMemberOfClass: is a test for class equality: [object isMemberOfClass: aClass] is only true if object is an instance of aClass. This test is done using the result of the -class method, which means that in this example model will test positive for [model isMemberOfClass: [NSObject class]].


It should be:

if([objctA isKindOfClass [MyClass class]])

Maybe can you use the 'NSStringFromClass' method and compare the obtained strings thanks to 'isEqualToString' ?