2

假设有一个像这样初始化的对象

- (void)doInit
{
    NSLog(@"In BaseClass init");
}

- (id)init
{
    self = [super init];
    [self doInit];

    return self;
}

它有一个以类似方式初始化的子类

- (void)doInit
{
    NSLog (@"In SubClass init");
}

- (id)init
{
    self = [super init];
    [self doInit];

    return self;
}

现在,如果我创建一个子类的实例,那么我会收到以下输出:

In SubClass init
In SubClass init

当真的,我的意思是

In BaseClass init
In SubClass init

有没有办法标记 doInit 说它不应该被覆盖,或者我需要为子类中的所有方法创建一个唯一的名称?

我不完全确定我以前怎么没有遇到过这个问题,但是你去吧。

编辑:

我理解为什么会发生这种情况,我没想到基类能够调用被覆盖的函数。

我也不能只调用 [super doInit]; 来自 Subclass 方法,因为 BaseClass 仍然需要调用 doInit 以便创建 BaseClass 的实例仍然有效。如果我调用 [super doInit],我最终还是会调用 SubClass 的 doInit 两次。

看来答案是否定的,我只需要像 doBaseClassInit 和 doSubClassInit 一样为每个 doInit 命名。

4

5 回答 5

4

如果您有一个不想通过动态绑定的方法(即不希望在存在子类方法时调用它),则需要将其作为 C 函数来执行。所以,你可以这样做:

在基类中:

static void DoInit(BaseClass *self)
{
    NSLog(@"In BaseClass init");
}

- (id)init
{
    self = [super init];
    if (self) {
        DoInit(self);
    }
    return self;
}

在子类中:

static void DoInit(SubClass *self)
{
    NSLog(@"In SubClass init");
}

- (id)init
{
    self = [super init];
    if (self) {
        DoInit(self);
    }
    return self;
}

请注意,这两种DoInit方法都标记为static,因此它们仅在每个编译单元(.m 文件)中可见,并且不会相互冲突。

于 2012-09-24T09:48:45.627 回答
2

也许你可以在你的基类中尝试这样的事情。这意味着任何时候init执行内部BaseClassdoInit实现,BaseClass都会调用 for 的实现。

- (void)doInit
{
    NSLog(@"In BaseClass init");
}

- (id)init
{
    self = [super init];

    Class baseClass = [BaseClass class];
    SEL selector = @selector(doInit);
    IMP baseClassImplementation = class_getInstanceMethod(baseClass, selector);
    baseClassImplementation(self, selector);

    return self;
}

正如我在评论中提到的那样,如果这是您需要的狭隘性,那么它应该可以解决发送消息所涉及的动态方法查找。希望这可以帮助!

编辑:

免责声明 - 如果您处于这种情况,这可能不是您设计寿命的好兆头。这项技术现在可以让您启动并运行,但请仔细记录它,并考虑重构代码的方法,以便不再使用它。考虑仅在极其紧急的情况下才真正使用此类修复。

于 2012-09-22T22:30:35.267 回答
1

您没有收到"In BaseClass init"控制台消息的原因是因为您的子类没有调用 super 的 doInit 实现。

如果您不想doInit被覆盖,避免这样做的“最佳”方法是不发布此方法的存在。从您的标题中删除它并唯一地命名该方法,以便不太可能发生冲突。例如,Apple 框架中的许多私有方法都有一个前导下划线。因此,例如,您可以调用您的方法_doInit,并且子类不太可能意外创建它自己的覆盖实现。

于 2012-09-22T21:41:43.083 回答
1

不,没有可强制执行的方法来防止子类覆盖方法。您可以做的最好的事情是避免将它放在类的公共头文件中,这样就不会有人试图覆盖它。如果该方法必须是公共的,您只需在该方法的文档中添加一条注释,说明它不应该被覆盖,或者无论哪种情况子类都应该调用 super 的实现。您会在 Apple 自己的类的文档中找到这些说明。

于 2012-09-22T21:44:14.743 回答
0

如果您希望您的子类使用 doInit 的基类版本,那么在子类中不要声明 doInit 方法。这就是我的意思:

A类:

@interface ClassA : 

-(void) doInit;
-(id) init;

@implementation

-(void) doInit {
    NSLog(@"ClassA doInit");
}

-(id) init {
    self = [super init];

    if (self != NULL)
        [self doInit];

    return self;
}

B类

@interface ClassB : ClassA

-(id) init;

@implementation

-(id) init {
    self = [super init];

    if (self != NULL)
        [self doInit];

    return self;
}

实际上,您也不需要重写 init 方法,除非您希望该类执行一些特殊代码。

于 2012-09-22T21:42:29.507 回答