在理解Objective-c中的动态绑定时卡住了



我刚刚开始学习Objective-C,我正在阅读Programming in Objective-C 3rd Edition by Stephen G. Kochan

有一段话解释了多态性机制:

在运行时,Objective-C运行时系统将检查存储在dataValue1(一个id对象)中的对象的实际类,并从正确的类中选择适当的方法来执行。但是,在更一般的情况下,编译器可能会生成不正确的代码来将参数传递给方法或处理其返回值。例如,如果一个方法以对象作为参数,而另一个方法以浮点值作为参数,就会发生这种情况。或如果一个方法返回一个对象而另一个返回一个整数,例如。如果两个方法之间的不一致只是不同类型的对象(例如,Fraction的add:方法接受一个Fraction对象作为参数并返回一个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:"的多个方法找到指向整数转换的不兼容指针将"id"发送给"int"类型的参数

我有Java和c++背景,我知道Objective-C中的多态性与那些语言略有不同,但我仍然对不确定性(粗体文本)感到困惑。

我想我一定是误解了什么,你能给我和那些需要的人更详细地解释一下Objective-C中的动态绑定吗?

谢谢你!

您没有注意到任何异常,因为这两个方法在例如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以不同方式处理的不同方法签名,并且如果没有足够的类型信息可用,则可能最终选择错误的方法签名。

之所以有区别,是因为在后一种情况下,区别只在于参数的类别。Complex*Fraction*都是指针,所以即使在两个同名方法之间存在混淆,也没有问题。

另一方面,您在示例中的情况是危险的,因为一个参数是指针,另一个参数是int。然而,要做到安全是很容易的:
NSLog(@"A: %i, B %i", [(A*)a add:100], [(B*)b add:b]);

相关内容

  • 没有找到相关文章

最新更新