Objective-C method signatures: Can parameter types differ between declaration and implementation?

I can declare a method in @interface with the parameter type NSString* :

 - (id) initWithString:(NSString*)str; 

and in implementation - NSNumber* :

 - (id) initWithString:(NSNumber*)str 

See the code below for a complete example. When calling [Work test] output is ax = Hi , so NSString* passed, and you could see that the "correct" initWithString method was called.

Why is this code accepted by the compiler?

Can I make the compiler complain when parameter types are different?

Quoting from Apple documentation Class definition :

The only requirement is that the signature matches, which means that you must keep the method name, as well as the parameters and return types exactly the same.

My test code is:

 @interface ClassA : NSObject @property (strong, nonatomic) NSNumber *x; - (id) initWithString:(NSString*)str; - (void) feed:(NSString*)str; @end @implementation ClassA - (id) initWithString:(NSNumber*)str { self = [super init]; if (self) { self.x = str; } return self; } - (void) feed:(NSNumber*)str { self.x = str; } @end @implementation Work + (void) test { ClassA *a = [[ClassA alloc] initWithString:@"Hi"]; NSLog(@"ax = %@", ax); } @end 

I added the feed method to see if it is "special" for init methods, but the compiler doesn't complain either. (Ran is on Yosemite / Xcode 6.4 / iOS8.4 Simulator.)

PS: If I did not use the correct conditions, please correct me :-)

+4
source share
3 answers

Can I make the compiler complain when parameter types differ?

There is a warning that you can activate by including the following line in the header:

 #pragma clang diagnostic error "-Wmethod-signatures" 

You can also put -Wmethod-signatures in the Other Warning Flags project to configure Xcode to activate it for the entire project.

I really don’t understand why Apple is so hesitant to activate useful alerts like this by default.

My standard scheme for almost every project is to put -Weverything in the “Other warning flags”. This activates all the warnings that clang can offer.

Since there are some warnings that are a little pedantic or do not serve my coding style, I individually deactivate unwanted types of warnings as they appear.

+2
source

In Objective-C, a method is defined as a string (called a selector) in the form doSomethingWithParam:anotherParam: Or in your case it will be initWithString: Note that there are no parameter types in these lines. One of the side effects of defining such methods is that Objective-C, unlike Java or C ++, does not allow operator overloading by simply changing the type of the parameter. Another side effect is the behavior you have observed.

EDIT: Also, it seems that the compiler does not generally look at the implementation when checking method calls, just an interface. Proof: declare the method in the header, do not specify any implementation for this method, and call this method from your code. This will compile just fine, but of course you will get an “unrecognized selector” exception when you run this code.

It would be great if someone could give a good explanation of the default compiler behavior.

0
source

I am surprised at the quote you found indicating that the param and return parameters are important for the uniqueness of the method signature. After re-reading, I think you found an error in the document .

Determining the type of parameter in the interface will trigger a warning for callers who do not pass this type (or pass this parameter to this type), regardless of implementation. Changing a parameter type in an implementation is exactly the same as casting a parameter inside a method. There is nothing wrong with that, not even a reason for warning. As long as different types share methods (polymorphic or inherited) with the declared type.

In other words, an example repetition ...

The following will lead to a compiler error, proving that different types of parameters do not give any difference to the compiler (the same is true for the return type) ...

 // .h - (void)foo:(NSString *)str; // .m - (void)foo:(NSString *)str { NSLog(@"called foo %@", [str class]); } - (void)foo:(NSNumber *)str { <----- duplicate declaration error } 

The following are compiler warnings, errors or runtime errors ...

 // .h - (void)foo:(NSString *)str; // .m - (void)foo:(NSNumber *)str { // everything implements 'class', so no problem here NSLog(@"called foo %@", [str class]); } 

The following is exactly like the previous example in every way ...

 // .h - (void)foo:(NSString *)str; // .m - (void)foo:(NSString *)str { NSNumber *number = (NSNumber *)str; NSLog(@"called foo %@", [number class]); } 

The following warnings will not cause, but will generate a runtime error, because we abuse the act by invoking a method that the passed type does not implement (assuming the caller calls with a string, as the interface indicates) ...

 // .h - (void)foo:(NSString *)str; // .m - (void)foo:(NSNumber *)str { NSLog(@"does str equal 2? %d", [str isEqualToNumber:@2]); <--- crash! } 

All of the above corresponds to intuition and behavior in practice, just the wrong passage in the dock. An interesting find!

0
source

All Articles