Mismatched -init method names
I discovered an odd scenario that produces a compiler warning in XCode that I don't believe is a valid warning.
As an example I created two classes, ClassA & ClassB, that both have an init method called -initWithSomething: One takes an (NSDate *) as the "something" and the other takes (NSString *)
Class A
// ClassA.h
#import <Foundation/Foundation.h>
@interface ClassA : NSObject {
}
-(id)initWithSomething:(NSDate *)something;
@end
// ClassA.m
#import "ClassA.h"
@implementation ClassA
-(id)initWithSomething:(NSDate *)something {
if (self = [super init]) {
}
return self;
}
@end
Class B
// ClassB.h
#import <Foundation/Foundation.h>
@interface ClassB : NSObject {
}
-(id)initWithSomething:(NSString *)something;
@end
// ClassB.m
#import "ClassB.h"
@implementation ClassB
-(id)initWithSomething:(NSString *)something {
if (self = [super init]) {
}
return self;
}
@end
Implementation of another class that uses both ClassA & ClassB
#import "ExampleClass.h"
#import "ClassA.h"
#import "ClassB.h"
@implementation ExampleClass
-(void)doSomething {
NSDate *date 开发者_如何转开发= [NSDate date];
NSString *string = [NSString stringWithFormat:@"Test"];
ClassA *classA = [[ClassA alloc] initWithSomething:date];
ClassB *classB = [[ClassB alloc] initWithSomething:string]; // Produces "Incompatible pointer types sending 'NSString *' to parameter of type 'NSDate *'
ClassB *classB2 = [[ClassB alloc] initWithSomething:[NSString stringWithFormat:@"Test"]]; // Does NOT produce a warning
ClassB *classB3 = [[ClassB alloc] initWithSomething:@"Test"]; // Produces the same warning as above.
[classA release];
[classB release];
[classB2 release];
[classB3 release];
}
Is this a compiler bug? Doesn't seem like either of those lines should produce a warning, especially since the line where "classB2" is initted produces no warnings.
This code actually works fine, the correct class' -initWithSomething: is called and passed the appropriate type of argument.Clearly, more explicit method names will avoid the issue but I'd like to know why the compiler isn't able to handle this.
Note: I should add that this only seems to occur with -init methods, any other instance or class functions do not seem to produce the warning.
I think that the problem is that +alloc
returns a generic id
.
This means that any method can be called on it, and the compiler will see that the first method imported that has the signature -initWithSomething
is for class A
, which expects an object of type NSDate *
.
Also, I do believe that the method +stringWithFormat
returns an id
which can be compatible with NSDate
.
EDIT:
A simple solution to this problem:
@interface ClassA
+(ClassA *) typeSafeAlloc;
// ...
@end
@implementation ClassA
+(ClassA *) typeSafeAlloc
{
// self is the class variable, which is the same as:
// return [ClassA alloc];
return [self alloc];
}
@end
And repeat the process with ClassB (with typeSafeAlloc returning a ClassB
object)
alloc return an object of type id and therefore the compiler assumes that initWithSomething
belongs to Class A
(The first class interface it encounters that has the method name).
Somthing like
[(ClassB*)[ClassB alloc] initWithSomething:string];
should solve the problem.
Look at the return type of +stringWithFormat
Returns a string created by using a given format string as a template into which the remaining argument values are substituted.
+ (id)stringWithFormat:(NSString *)format, ...
(from http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html)
The compiler knows string is an NSString*
, same with @"literals"
. An id
however, could be anything (even an NSSDate*
).
精彩评论