我尝试使用 id
在 Objective-C 中创建鸭子类型。这个概念在理论上看起来不错,但在实践中却失败了。我无法在我的方法中使用任何参数。方法已调用,但参数错误。我得到了对象的 BAD_ACESS 和基元的随机值。我在下面附上了一个简单的示例。
问题: 有谁知道为什么方法参数错误? Objective-c 的底层发生了什么?
注意:我对细节感兴趣。我知道如何使下面的示例起作用。
示例:
我创建了一个简单的类 Test
,它使用属性 id test
传递给其他类。
@implementation Test
- (void) aSampleMethodWithFloat:(float) f andInt: (int) i {
NSLog(@"Parameters: %f, %i\n", f, i);
}
@end
然后在类中执行以下循环:
for (int i=0; i < 10; ++i) {
float f=i*0.1f;
[tst aSampleMethodWithFloat:f andInt:i]; // warning no method found.
}
这是我得到的输出。正如您所看到的,该方法被调用,但参数错误。
Parameters: 0.000000, 0
Parameters: -0.000000, 1069128089
Parameters: -0.000000, 1070176665
Parameters: 2.000000, 1070805811
Parameters: -0.000000, 1071225241
Parameters: 0.000000, 1071644672
Parameters: 2.000000, 1071854387
Parameters: 36893488147419103232.000000, 1072064102
Parameters: -0.000000, 1072273817
Parameters: -36893488147419103232.000000, 1072483532
更新:
我偶然发现,当我使用 for
循环向类添加 aSampleMethodWith...
声明时,警告消失并且测试中的方法消失类被正确调用。
更新 2: 正如 JeremyP 所指出的,问题的直接原因是 float 被视为 double 。但有人知道为什么吗? (遵循 5why 原则:))。
根据 @eman 的说法,该调用被转换为简单的 C 函数调用和编译器指令以获取 SEL
。所以@selector 会感到困惑。但为什么?编译器在第一个方法调用中拥有所有必需的类型信息。有谁知道有关 Objective-C 内部结构的良好信息来源吗?我搜索过 Objective-C 编程语言,但没有找到答案。
最佳答案
默认情况下,浮点值作为 double 而不是 float 传递。编译器不知道,在 [tst aSampleMethodWithFloat:f andInt:i];
发生时,它只应该传递一个浮点型,因此它将 f 提升为 double 型。这意味着,在该方法中,当编译器知道它正在处理浮点型时,f 是由传递给该方法的 double 的前四个字节形成的浮点型,而 i 是由传递给该方法的 double 形成的 int 型从传递的 double 的后四个字节开始。
您可以通过以下任一方式解决此问题
- 将 aSampleMethodWithFloat:andInt: 的第一个参数更改为 double
- 将 Test 的接口(interface)声明导入到您使用它的文件中。
注意,在 C 中使用 float 时,除了占用少量空间外,没有任何好处。您还不如在任何地方使用 double 。
关于objective-c - ObjC 内部结构。为什么我的鸭子打字尝试失败了?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2709383/