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).
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With