3
votes

I have just started learning Objective-C, I am reading Programming in Objective-C 3rd Edition by Stephen G. Kochan.

There's a paragraph explaining the polymorphism mechanism:

At runtime, the Objective-C runtime system will check the actual class of the object stored inside dataValue1(an id object) and select the appropriate method from the correct class to execute. However, in a more general case, the compiler might generate the incorrect code to pass arguments to a method or handle its return value.This would happen if one method took an object as its argument and the other took a floating-point value, for example. Or if one method returned an object and the other returned an integer, for example. If the inconsistency between two methods is just a different type of object (for example, the Fraction’s add: method takes a Fraction object as its argument and returns one, and the Complex’s add: method takes and returns a Complex object), the compiler will still generate the correct code because memory addresses (that is, pointers) are passed as references to objects anyway.

I don't quite get it that the first part of the paragraph says that the compiler might generate incorrect code if I declare 2 methods in different classes with the same name and different type of arguments. while the last part of the paragraph says that it is fine to have 2 methods with the same name and different arguments and return types...oh no...

I have the following code and they compile and run fine:

@implementation A
- (int) add:(int)a {
    return 1 + a;
}
@end
@implementation B
- (int) add: (B*) b {
    return 100;
}
@end
id a = [[A alloc] init];
id b = [[B alloc] init];
NSLog(@"A: %i, B %i", [a add:100], [b add:b]);

Edit: As the text I cited, the code above is supposed to cause errors, but it only produces some warning messages, Multiple methods named "add:" found, Incompatible pointer to integer conversion sending "id" to parameter of type "int"

I have Java and C++ background, I know polymorphism in Objective-C is slightly different from that of those languages but I am still confused about the uncertainty(text in bold).

I think I must have misunderstood something, would you please explain in more detail about dynamic binding in Objective-C for me and those who need it?

Thanks you!

2
Related: What's the need of Informal Protocols?.user557219

2 Answers

6
votes

You haven’t noticed anything unusual because those two methods have the same call semantics under, for example, the x86_64 ABI. Pointers can be considered integers and, under the x86_64 ABI, they are passed to the target method in the same manner.

However, if you had another class, e.g.:

@implementation C
- (int)add:(float)number {
    return (int)number + 100;
}
@end

receiving a floating-point argument (as mentioned by Kochan), then the compiler, when parsing:

id a = [[A alloc] init];
id b = [[B alloc] init];
id c = [[C alloc] init];
NSLog(@"A: %i, B %i, C %i", [a add:100], [b add:b], [c add:100]);

wouldn’t know that for [c add:100] it should place 100 in a floating-point register as specified by the x86_64 ABI. Consequently, -[C add:], which expects the floating-point argument to be in a floating-point register, reads a value that doesn’t correspond to the 100 argument.

For it to work, you would have to either declare the variable with a static type:

C *c = [[C alloc] init];

or cast it to the correct type when sending a message:

[(C *)c add:100];

At the end of the day, sending an Objective-C message is a function call. Different ABIs may have different semantics for calling functions with variable arguments, floating-point vs. integer arguments or return values, or structures instead of scalar arithmetic types. If the compiler sees different method signatures that are handled differently according to the target ABI, and if there is not enough type information available, it may end up choosing the wrong method signature.

3
votes

The distinction is made because in the latter case, the difference is only in the class of the arguments. Complex* and Fraction* are both pointers, so even if there's confusion between the two identically-named methods, there's no issue.

The situation you have in your example, on the other hand, is dangerous, because one argument is a pointer and the other is an int. Being safe about this is easy, however:

NSLog(@"A: %i, B %i", [(A*)a add:100], [(B*)b add:b]);