Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-C class_conformsToProtocol() bug?

I have come across some strange behavior in my iPhone Objective-C app.

I am using some code to test an object:

if (!class_conformsToProtocol([someVar someFunctionThatReturnsAClass], @protocol(MyProtocol)))
     [NSException raise:@"Invalid Argument" format:@"The variables returned by 'someFunctionThatReturnsAClass' Must conform to the 'myProtocol' protocol in this case."];

Oddly, when I have a class that looks like this:

@interface BaseClass : NSObject<MyProtocol>

...

@end

@interface SubClass : BaseClass

...

@end

And when I call this fragment: class_conformsToProtocol([SubClass class], @protocol(MyProtocol)), it returns NO.

Also, this code fails:

class_conformsToProtocol([NSString class], @protocol(NSObject)); // also returns NO

While this code returns YES:

[NSString conformsToProtocol:@protocol(NSObject)];

Is there anything I am missing in the docs? Or is this a bug of some sort? (I am on iOS 4.2 if that matters any).

like image 443
Richard J. Ross III Avatar asked Feb 11 '11 23:02

Richard J. Ross III


2 Answers

If there's a bug here, it's in the documentation.

According to the source, class_conformsToProtocol() uses class_copyProtocolList() and then tests each resulting protocol against the parameter. class_copyProtocolList() is documented as only returning protocols that the given class adopts, but not protocols adopted by superclasses. class_conformsToProtocol() therefore only tests if the given class adopts a protocol and not if its superclasses do.

The documentation bug is that class_conformsToProtocol() doesn't state this behavior. However, the documentation does state that you should generally not use that function, but instead use NSObject's conformsToProtocol: method instead.

like image 150
Lily Ballard Avatar answered Nov 07 '22 22:11

Lily Ballard


Use NSObject's conformsToProtocol: method.

Here's an experiment I tried:

@protocol MyProtocol

- (void) doSomething;

@end

@interface MyClass : NSObject<MyProtocol>
{
}

@end

@implementation MyClass

- (void) doSomething { 
}

@end

@interface MyOtherClass : MyClass
{

}

@end

@implementation MyOtherClass

- (void) doSomething {
}

@end


int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    MyClass *obj_one = [MyClass new];
    BOOL one_conforms = [obj_one conformsToProtocol:@protocol(MyProtocol)];

    MyOtherClass *obj_two = [MyOtherClass new];
    BOOL two_conforms  = [obj_two conformsToProtocol:@protocol(MyProtocol)];
    NSLog(@"obj_one conformsToProtocol: %d", one_conforms);
    NSLog(@"obj_two conformsToProtocol: %d", two_conforms);

    [pool drain];
    return 0;
}

Output:

obj_one conformsToProtocol: 1
obj_two conformsToProtocol: 1

Whereas:

MyOtherClass *obj_two = [MyOtherClass new];
BOOL conforms_two = class_conformsToProtocol([obj_two class], @protocol(MyProtocol));
NSLog(@"obj_two conformsToProtocol: %d", conforms_two);

Output:

obj_two conformsToProtocol: 0

Verdict:

This is a bug with class_conformsToProtocol, use the conformsToProtocol: method of NSObject

Unlike class_conformsToProtocol, NSObject's conformsToProtocol: method will check superclasses as well.

like image 40
Jacob Relkin Avatar answered Nov 07 '22 21:11

Jacob Relkin