views:

39

answers:

2

I have a class called AddressCard from an example in "Programming in Objective C", and I'm implementing a isEqual: method.

The signature of this method in NSObject uses loose typing for the parameter:

- (BOOL)isEqual:(id)anObject

OTOH, the sample code in the book uses strict typing:

- (BOOL) isEqual:(AddressCard *) aCard

I'm not sure I fully understand what the compiler does in this case. I tried comparing an AddressCard to a NSString ([aCard isEqual: @"Foo"]) expecting either a runtime error (if the system uses my method) or that the system would call NSObject's version of IsEqual.

Instead, my method was called (even though the parameter was a NSString and not an AddressCard) and raised an exception when my IsEqual: tried to call a a method specific to AddressCard:

- (BOOL) isEqual:(AddressCard *) aCard {
    if ([name isEqualToString: [aCard name]] && /*here I get the error*/
        [email isEqualToString:[aCard email]]) {
        return YES;
    }else {
        return NO;
    }
}

What's going on? How on Earth is an NSString being passed to a method that expects something else? Is changing the signature of a method OK when overriding it?

+1  A: 

My best guess: All the compiler sees is a method that expects a pointer gets called with a pointer parameter. No problems for the compiler.

Altealice
+1  A: 

The runtime distinguishes messages by their selector. All methods with the same name have the same selector. Method arguments have no influence on the selector. In your case, the selector is isEqual:.

This is from Apple's "The Objective-C Programming Language" (emphasis mine):

The messaging routine has access to method implementations only through selectors, so it treats all methods with the same selector alike. It discovers the return type of a method, and the data types of its arguments, from the selector. Therefore, except for messages sent to statically typed receivers, dynamic binding requires all implementations of identically named methods to have the same return type and the same argument types. (Statically typed receivers are an exception to this rule, since the compiler can learn about the method implementation from the class type.)

In other words: changing the signature of an existing method is not good form (IMO) but it's fine as long as you statically type the receivers of these methods (in your case, that means aCard must be declared as AddressCard *). For the runtime, this is no problem.

Unfortunately, you are not mentioning whether the compiler gives you a warning because you are passing an NSString * where it expects an AddressCard *. I would expect it to do so.

Ole Begemann
No warning at all. It just raises an exception in runtime.
Fernando
I would definitely expect a warning too. Seems a bit sloppy...
Fernando
BTW, if I change the signature of my isEqual: method, so it matches NSObject's I do get a warning: "Conflicting types for '-(BOOL)isEqual:(id*)aCard. Any ideas? O:-)
Fernando
No, that's because the type of the argument should be `id` and not `id *`. Different topic.
Ole Begemann
Ouch! You're correct. Thanks. O:-)
Fernando