【问题标题】:Stuck in understanding dynamic binding in Objective-c坚持理解 Objective-c 中的动态绑定
【发布时间】:2011-11-06 04:04:56
【问题描述】:

我刚刚开始学习 Objective-C,我正在阅读 Stephen G. KochanObjective-C 3rd Edition 编程

有一段解释多态机制:

在运行时,Objective-C 运行时系统会检查存储在 dataValue1 中的对象的实际类(一个 id 对象),并从正确的类中选择适当的方法来执行。但是,在更一般的情况下,编译器可能生成不正确的代码来将参数传递给方法或处理其返回值。如果一个方法接受一个对象,就会发生这种情况例如,作为它的参数,另一个采用浮点值。或者 例如,如果一个方法返回一个对象而另一个返回一个整数。如果两个方法之间的不一致只是对象类型不同(例如,Fraction 的 add: 方法将 Fraction 对象作为其参数并返回一个,而 Complex 的 add: 方法采用并返回一个 Complex 对象),编译器将仍然会生成正确的代码,因为内存地址(即指针)无论如何都是作为对对象的引用传递的。

我不太明白该段的第一部分说如果我在具有相同名称和不同类型参数的不同类中声明 2 个方法,编译器可能会生成不正确的代码。而该段的最后一部分说可以有 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 中的动态绑定?

谢谢!

【问题讨论】:

标签: objective-c dynamic-binding


【解决方案1】:

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

【讨论】:

  • 感谢您的意见。我的困惑现在被清除了。但这也让我觉得 Objective-C 中的动态绑定并不那么安全。
  • 只需确保在发送类型不明确的消息之前显式转换任何变量。请注意,由于 Cocoa 的命名约定,大多数参数都被描述(stringByAppendingString:numberWithFloat:),因此很少有同名但不同类型的方法。
  • @Neevek 除了andyvnn22所说的,如果你声明了具有相同名称(选择器)和不同签名的方法,编译器会警告你。
【解决方案2】:

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

另一方面,您在示例中的情况很危险,因为一个参数是指针,另一个是int。但是,确保安全很容易:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-23
    • 1970-01-01
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多