我刚刚开始学习Objective-C,我正在阅读 Stephen G. Kochan
有一段解释多态性机制:
在运行时,Objective-C运行时系统将检查存储在 dataValue1 (id对象)中的对象的实际类,并从正确的类中选择适当的方法来执行。但是,在更一般的情况下,编译器可能生成错误的代码以将参数传递给方法或处理其返回值。如果一个方法占用一个对象,就会发生这种情况例如,作为其参数,另一个采用浮点值。要么 例如,如果一个方法返回一个对象而另一个方法返回一个整数。如果两个方法之间的不一致只是一个不同类型的对象(例如,Fraction的add:方法将Fraction对象作为其参数并返回一个,而Complex的add:方法接受并返回一个Complex对象),编译器将仍会生成正确的代码,因为内存地址(即指针)无论如何都会作为对象的引用传递。
如果我在具有相同名称和不同类型参数的不同类中声明2个方法,那么段落的第一部分说明编译器可能生成错误的代码,我不太明白。虽然段落的最后一部分说有两个方法具有相同的名称和不同的参数以及返回类型是好的...哦不...
我有以下代码,他们编译并运行良好:
@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]);
编辑: 正如我引用的文本,上面的代码应该会导致错误,但它只会产生一些警告消息,多个方法名为“add:”found ,指向整数转换的不兼容指针发送“id “到”int“类型的参数
我有Java和C ++背景,我知道Objective-C中的多态性与那些语言略有不同,但我仍然对不确定性感到困惑(粗体文本)。
我想我一定是误解了一些东西,请你详细解释一下Objective-C中我和那些需要动态绑定的人吗?
谢谢!
答案 0 :(得分:6)
您没有注意到任何异常,因为这两个方法在例如x86_64 ABI下具有相同的调用语义。指针可以被认为是整数,并且在x86_64 ABI下,它们以相同的方式传递给目标方法。
但是,如果你有另一个类,例如:
@implementation C
- (int)add:(float)number {
return (int)number + 100;
}
@end
在解析时接收浮点参数(如Kochan所述),然后是编译器:
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]);
不知道对于[c add:100]
,它应该将100
放在x86_64 ABI指定的浮点寄存器中。因此,-[C add:]
期望浮点参数位于浮点寄存器中,读取的值与100
参数不对应。
要使其工作,您必须使用静态类型声明变量:
C *c = [[C alloc] init];
或在发送消息时将其强制转换为正确的类型:
[(C *)c add:100];
在一天结束时,发送一个Objective-C消息是一个函数调用。不同的ABI可能具有不同的语义,用于调用具有变量参数,浮点数与整数参数或返回值的函数,或结构而不是标量算术类型。如果编译器看到根据目标ABI以不同方式处理的不同方法签名,并且如果没有足够的类型信息可用,则最终可能会选择错误的方法签名。
答案 1 :(得分:3)
之所以区别,是因为在后一种情况下,区别仅在于参数的类。 Complex*
和Fraction*
都是指针,所以即使两个同名方法之间存在混淆,也没有问题。
另一方面,您的示例中的情况很危险,因为一个参数是指针而另一个是int
。然而,保持安全是很容易的:
NSLog(@"A: %i, B %i", [(A*)a add:100], [(B*)b add:b]);