gpt4 book ai didi

初始化时 alloc 返回的 Objective-C 类与错误的类混淆

转载 作者:太空狗 更新时间:2023-10-30 03:49:42 26 4
gpt4 key购买 nike

就 alloc 和 init... 方法而言,我认为我了解基本的 Objective-C,但显然我不了解。我已经将我遇到的问题归结为下面的最小示例。 (例如,我将所有源代码放入一个文件中,但如果源代码像通常那样被拆分为多个源文件和头文件,问题也会发生。

以下是代码的作用以及运行时发生的情况的概要。

我定义了两个类 MyInteger 和 MyFloat,它们几乎相同,只是一个处理 int 类型,另一个处理 float 类型。两者都有名为 initWithValue: 的初始化方法,但参数类型不同。有一个#define 来控制是否定义了 MyInteger 类,原因是,即使从未使用过该类,它也会导致程序的不同行为。

main() 仅使用 MyFloat。前两行这样做:分配一个 MyFloat 实例,并用值 50.0 初始化它。然后打印该值。根据是否定义了 MyInteger,我得到两个不同的输出。

没有定义 MyInteger,正如我所期望的那样:


[4138:903] float parameter value:50.000000
[4138:903] Value:50.000000
(next two output lines omitted)

定义了 MyInteger 后,令我大吃一惊:


[4192:903] float parameter value:0.000000
[4192:903] Value:0.000000
(next two output lines omitted)

在我看来,编译器将对 initWithValue: 的调用视为属于 MyInteger 类。接下来的两行 main() 通过将 [MyFloat alloc] 强制转换为类型 MyFloat* 来对此进行测试。即使定义了 MyInteger,它也确实会按预期生成输出:


[4296:903] float parameter value:0.000000
[4296:903] Value:0.000000
[4296:903] float parameter value:50.000000
[4296:903] Value with cast:50.000000

请解释这是怎么回事!我已经为它苦苦挣扎了超过 24 小时,甚至到了打开门让一些热量散发出去的地步,这样我的电脑就可以冷却下来:-)谢谢!

另一个奇怪的是,如果我将 MyInteger 的定义移到 MyFloat 的定义之下,那么一切都“很好”——如我预期的那样工作。历史经常证明我错了,以至于我怀疑编译器是罪魁祸首。无论如何,这里是编译器和项目信息:使用 Xcode 4.0.2。尝试了所有 3 个编译器选项(GCC 4.2、LLVM GCC 4.2 和 LLVM Compiler 2.0)。本示例的 Xcode 项目是使用基于 Foundation 的 Mac OS X 命令行工具的标准配置设置的。


#define DO_DEFINE_MYINTEGER 1

//------------- define MyInteger --------------
#if DO_DEFINE_MYINTEGER
@interface MyInteger : NSObject {
int _value;
}
-(id)initWithValue:(int)value;
@end

@implementation MyInteger
-(id)initWithValue:(int)value {
self= [super init];
if (self) {
_value= value;
}
return self;
}
@end
#endif


//------------- define MyFloat --------------
@interface MyFloat : NSObject {
float _value;
}
-(id)initWithValue:(float)value;
-(float)theValue;
@end

@implementation MyFloat
-(id)initWithValue:(float)value {
self= [super init];
if (self) {
NSLog(@"float parameter value:%f",value);
_value= value;
}
return self;
}
-(float)theValue {
return _value;
}
@end

//--------------- main ------------------------
int main (int argc, const char * argv[])
{
MyFloat *mf1= [[MyFloat alloc] initWithValue:50.0f];
NSLog(@"Value:%f",[mf1 theValue]);

MyFloat *mf2= [((MyFloat*)[MyFloat alloc]) initWithValue:50.0f];
NSLog(@"Value with cast:%f",[mf2 theValue]);

return 0;
}

最佳答案

+alloc 的原型(prototype)是返回 id,当编译器面临多个 -initWithValue: 方法的选择时,它生成调用它找到的第一个的代码。当定义 MyInteger 时,这意味着编译器将生成代码来转换 50.0 并将其作为整数参数传递。请注意,整数和浮点参数的传递方式不同,前者在堆栈中,后者在浮点寄存器中。

在运行时,因为消息分发是动态处理的,所以会调用正确的方法 - 但该方法假定 value 参数是在浮点寄存器中传递的。但这不是调用代码放置它的位置,因此被调用方法在读取该寄存器时会得到不正确的结果。

类型转换之所以有效,是因为它显式地告诉编译器将在运行时调用哪些方法,这允许它生成正确的调用代码,而不是将 value 传递到浮点寄存器中在堆栈上。

编辑:综上所述,NSResponder 的回答也提出了一些非常好的观点。在 Objective-C 中,声明共享相同名称但具有不同签名(即参数和返回类型)的方法是一个非常糟糕的主意,并且名为 -initWithValue: 的方法暗示其参数是一个 NSValue 对象。

关于初始化时 alloc 返回的 Objective-C 类与错误的类混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5765743/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com