jtalarico
jtalarico

Reputation: 1066

Odd or erroneous warning with multiple @protocols

I've got an odd situation where I've got two protocols and a couple classes that are initialized with delegates which implement the protocols. Standard stuff, really. However, XCode 4 is tossing up erroneous warnings that I can't seem to wrap my head around. Here's the collapsed source listing showing the definitions and implementations.

// CLASS A
@protocol ClassADelegate <NSObject>
-(void) DoSomething;
@end

@interface ClassA : NSObject {  }
+(ClassA *) classWithDelegate:(id<ClassADelegate>)d;
-(ClassA *) initWithDelegate:(id<ClassADelegate>)d;
@end

@implementation ClassA
+(ClassA *) classWithDelegate:(id<ClassADelegate>)d {
     return [[[ClassA alloc]initWithDelegate:d]autorelease];
}
-(ClassA *) initWithDelegate:(id<ClassADelegate>)d{
     self = [super init];
     return self;
}
@end

// CLASS B
@protocol ClassBDelegate <NSObject>
-(void) DoSomethingElse;
@end

@interface ClassB : NSObject<ClassADelegate> {  }
+(ClassB *) classWithDelegate:(id<ClassBDelegate>)d;
-(ClassB *) initWithDelegate:(id<ClassBDelegate>)d;
-(void) DoSomething;
@end

@implementation ClassB    
+(ClassB *) classWithDelegate:(id<ClassBDelegate>)d {
    return [[[ClassB alloc]initWithDelegate:d]autorelease];
}
-(ClassB *) initWithDelegate:(id<ClassBDelegate>)d{
    self = [super init];
    return self;
}
-(void) DoSomething {

}
@end

Now here's the crazy part -- in the static constructor of ClassB on this line:

return [[[ClassB alloc]initWithDelegate:d]autorelease];

...the compiler is throwing a warning that doesn't make sense:

"Type 'id <ClassBDelegate>' does not conform to the 'ClassADelegate' protocol"

Am I missing something? The selector clearly sets the type to id<ClassBDelegate>, so d should be correct. Is this just the compiler getting confused?

Upvotes: 4

Views: 350

Answers (1)

user557219
user557219

Reputation:

The compiler is getting confused because you have methods that have the same name but different return/parameter types, and Objective-C is not exactly friends with method overloading.

When the compiler analyses:

[[[ClassB alloc]initWithDelegate:d]autorelease];

it starts with:

[ClassB alloc]

and the return type of +[ClassB alloc] is id, so the expression above is of type id. The next step is to analyse:

[##expression of type id## initWithDelegate:d]

and at this point there are two possible methods:

-(ClassA *) initWithDelegate:(id<ClassADelegate>)d;
-(ClassB *) initWithDelegate:(id<ClassBDelegate>)d;

Both are possible because the receiver is of type id, so it could be either a ClassA instance or a ClassB instance. But, aha!, the protocol of the first parameter differs, so there shouldn’t be any confusion. However, method lookup is based on two variables: whether it’s a class or instance method, and the corresponding selector. In the methods above, both are instance methods and both have the same selector. The compiler has decided to pick the first one.

One fix is to tell the compiler that the object returned by [ClassB alloc] is of type ClassB * instead of the generic type id:

return [[(ClassB *)[ClassB alloc]initWithDelegate:d]autorelease];

and maybe use this pattern in all similar classes.

Another fix is to give those methods different names, e.g. -initWithClassADelegate: and -initWithClassBDelegate:.

Upvotes: 4

Related Questions