【问题标题】:Are Objective-C initializers allowed to share the same name?是否允许 Objective-C 初始化程序共享相同的名称?
【发布时间】:2010-04-27 01:56:57
【问题描述】:

当我有两个使用同名但类型不同的参数的初始化程序的类时,我在 Objective-C 中遇到了一个奇怪的问题。例如,假设我创建了 A 类和 B 类:

啊:

#import <Cocoa/Cocoa.h>

@interface A : NSObject {
}

- (id)initWithNum:(float)theNum;

@end

上午:

#import "A.h"

@implementation A

- (id)initWithNum:(float)theNum
{
    self = [super init];
    if (self != nil) {
        NSLog(@"A: %f", theNum);
    }
    return self;
}

@end

B.h:

#import <Cocoa/Cocoa.h>

@interface B : NSObject { 
}

- (id)initWithNum:(int)theNum;

@end

B.m:

#import "B.h"

@implementation B

- (id)initWithNum:(int)theNum
{
    self = [super init];
    if (self != nil) {
        NSLog(@"B: %d", theNum);
    }
    return self;
}

@end

ma​​in.m:

#import <Foundation/Foundation.h>

#import "A.h"
#import "B.h"

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    A *a = [[A alloc] initWithNum:20.0f];   
    B *b = [[B alloc] initWithNum:10];

    [a release];
    [b release];

    [pool drain];
    return 0;
}

当我运行它时,我得到以下输出:

2010-04-26 20:44:06.820 FnTest[14617:a0f] A: 20.000000
2010-04-26 20:44:06.823 FnTest[14617:a0f] B: 1

如果我颠倒导入的顺序,使其首先导入 B.h,我会得到:

2010-04-26 20:45:03.034 FnTest[14635:a0f] A: 0.000000
2010-04-26 20:45:03.038 FnTest[14635:a0f] B: 10

由于某种原因,它似乎使用了在两个类中首先包含的 @interface 中定义的数据类型。我通过调试器进行了一些单步调试,发现 a 和 b 对象的 isa 指针最终相同。我还发现,如果我不再使 alloc 和 init 调用内联,这两个初始化似乎都可以正常工作,例如:

A *a = [A alloc];
[a initWithNum:20.0f];

如果我在创建 a 和 b 时都使用此约定,我会得到正确的输出,并且每个对象的 isa 指针似乎都不同。

我做错了吗?我原以为多个类可以具有相同的初始化程序名称,但也许不是这样。

【问题讨论】:

    标签: objective-c selector initializer


    【解决方案1】:

    问题在于+alloc 方法返回id 类型的对象,因此编译器无法决定使用哪个方法签名。您可以通过多种方式强制您的应用程序选择正确的选择器。一种是从 alloc 中转换返回值,所以:

    A* a = [(A*)[A alloc] initWithNum:20.f];
    B* b = [(B*)[B alloc] initWithNum:10];
    

    或者您可以在您的类上覆盖 alloc 并返回更具体的内容,尽管我自己不会这样做。所以:

    + (A*)alloc { return [super alloc]; }
    

    最后,我个人会选择什么,让选择器更具描述性:

    // A.h
    - (id)initWithFloat:(float)theNum;
    
    // B.h
    - (id)initWithInteger:(int)theNum;
    

    【讨论】:

    • 您还可以添加静态typeWithArgument: 方法,例如[NSNumber numberWithInt:] 来消除歧义。
    • 根据文档,当您调用+[NSObject alloc] 时应设置isa ivar。既然 Objective-C 消息分发是在运行时解析的,那么计算 +[A alloc] 解析到什么不是运行时的工作,而不是编译器的工作吗?
    • @Nick Forge:实现是动态绑定的,所以这不是问题(这在此处适用,因为 NSLog 按预期显示 A 和 B)。编译器参与的地方是构建对 obj_msgSend 的调用。在这种情况下,一个选择器使用浮点数,而另一个选择器使用整数,编译器生成不同的指令(将 int 存储在整数寄存器中,将浮点数存储在 fp 寄存器中)。因此,该方法的实现会在它在预期的寄存器中查找时发现错误数据。
    • @drawnonward:我看不出这与我说我将使用的最后一点(即 initWithFloat:) 有什么不同。还是您的意思是在 init 方法中使用实际的 NSNumber 而不是标量值(例如,initWithNumber:(NSNumber*)theNum)?
    • @Jason Coco 这很有意义。您的回答不应该使这更清楚吗?你说“所以编译器无法决定调用哪个选择器”,实际上问题在于参数类型,而不是选择器,因为选择器/IMP 解析发生在运行时,它是int/@987654332 @ 导致错误的参数不匹配。鉴于此,使用像 initWithFloat: 这样的初始化程序不会造成问题,因为每个(理智的)initWithFloat: 初始化程序都会将 float 作为参数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-04
    • 2013-04-29
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 2019-08-12
    相关资源
    最近更新 更多