views:

798

answers:

1

I have a subclass of NSObject that implements an -(id)initWithRootElement:(MyElement *)e method. NSXMLDocument has an identical method that takes an NSXMLElement. When I compile, I get the following warning:

warning: incompatible Objective-C types 'struct MyElement *', expected 'struct NSXMLElement *' when passing argument 1 of 'initWithRootElement:' from distinct Objective-C type

In this case, I'm compiling with Clang + LLVM on SnowLeopard with Xcode 3.2.1, but this also happens with GCC 4.2 on both Leopard and SnowLeopard.

What I don't understand is why it's throwing a warning for my direct NSObject subclass when NSXMLDocument has to inherit from NSXMLNode first? Shouldn't it know that -(id)initWithRootElement:(NSXMLElement *)e only applies to NSXMLDocument which has nothing to do with my class? I could understand if I was trying to overload the method, but I'm not. Please tell me I'm not going crazy...

#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSXMLElement.h>
// Importing this here causes the warning...
// #import <Foundation/NSXMLDocument.h>

typedef NSObject MyElement;

@interface TestClass : NSObject
{
}

- (id)initWithRootElement:(MyElement *)element;
@end

@implementation TestClass
- (id)initWithRootElement:(MyElement *)element { return nil; }
@end

// ...but here it doesn't
// #import <Foundation/NSXMLDocument.h>

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

    // No warning! Inheritance: NSXMLDocument -> NSXMLNode -> NSObject
    NSXMLElement *xmlElement = [[NSXMLElement alloc] initWithName:@"foo"];
    [[TestClass alloc] initWithRootElement:xmlElement];

    // warning: incompatible Objective-C types 'struct MyElement *', expected 'struct NSXMLElement *' when passing argument 1 of 'initWithRootElement:' from distinct Objective-C type
    MyElement *element = [[MyElement alloc] init];
    [[TestClass alloc] initWithRootElement:element];

    [pool drain];
    return 0;
}
+5  A: 

Objective-C doesn't support covariant declarations.

The declaration of initWithRootElement: in NSXMLDocument is as follows:

- (id)initWithRootElement:(NSXMLElement *)element;

This is different than your declaration:

- (id)initWithRootElement:(MyElement *)element;

In that the argument types are different. This causes confusion in this line of code (where element is of type MyElement *...

[[TestClass alloc] initWithRootElement:element];

... because the return type of +alloc is id and, thus, the compiler doesn't know which method to use; which type of argument is to be expected.

When developing code using Objective-C targeting Apple's frameworks, the rule of thumb is to never have different arguments declared for any given selector.

I'm slightly surprised that the first case doesn't also warn. It likely should. If you have a fairly minimal test case, file a bug via http://bugreport.apple.com/ and append the bug # to this question (or my answer).

bbum
Thanks Bill! I completely forgot that +alloc returned id. Makes perfect sense now.I definitely intend on changing the method name, but got perturbed when I couldn't figure out why it was warning me about that.
Matt Lilek