0

Parent.h(扩展 NSObject)像这样:

@implementation InTParent

-(id)init
{
    NSLog(@"Parent init method");
    if (self = [super init]) {
        ;
    }
    return self;
}


-(id)initWithName:(NSString*)name;
{
    NSLog(@"Parent initWithName method");
    if (self = [self init]) {
        ;
    } 
    return self;
}

Son.h(extends Parent) 像这样:

@implementation InTSon

-(id)init
{
    NSLog(@"Son init method");
    if (self = [super init]) {
        ;
    }
    return self;
}

-(id)initWithName:(NSString*)name;
{
    NSLog(@"Son initWithName method");
    if (self = [super initWithName:name]) {
        ;
    }
    return self;
}

我用这个:IntSon *_son = [[IntSon alloc] initWithName:@"asd"];

为什么输出是:Son initWithName 方法 --> Parent initWithName 方法 --> Son init 方法 --> Parent init 方法

但是在 Java 中,它可能是这样的:子 initWithName 方法 --> 父 initWithName 方法 --> 父 init 方法

请帮我!

4

3 回答 3

2

要了解这种行为,您必须了解 Objective-C 消息调度的工作原理。这是一个很好的例子来说明它。

在高层次上,每当您在任何对象上调用方法时,Objective-C 运行时都会查找提供最多派生(在类层次结构中最深)的类的实现。如果它没有找到它,它将转到下一个最衍生的,依此类推,直到NSObject. 当它第一次找到与选择器匹配的实现(大致方法名称)时,它将执行该实现。当您调用 时super,您指定将消息发送到该方法的下一个最派生类的实现。

因此,在您的代码中,您调用类allocInTSon该类返回一个实例,IntSonisa指针设置为类 object InTSonisa指针是在提升类层次结构的过程中查找方法实现的方式。

所以在你有一个InTSon实例之后,你调用initWithName:它,它会检查指向的类isa(这是InTSon这个方法的实现。它找到它,所以执行它,产生你的第一个输出:

"Son initWithName method"

紧接着,您调用该方法的超类实现,它会查找并执行该代码InTParent的实现initWithName:,从而产生第二个输出:

Parent initWithName method

现在在这里您可以看到与 Java 的偏差 - 您可以调用init。但是,它是指向实例的指针。因此,当运行时解析此消息时,它首先会在类中查找 的实现。当然,它会找到它并执行该方法的代码,这会为您提供第三个输出,. 接下来调用 super,它会查找并执行 的实现,并为您提供最终输出。selfselfInTSoninitInTSonSon init methodInTParentinit

总而言之,无论从类层次结构中的哪个位置调用方法,如果调用self它,它将始终执行该方法的最派生实现。希望这会有所帮助,如果您有任何问题,请告诉我!

于 2012-09-07T03:03:20.760 回答
1

您的类图如下所示:

类图

当您将initWithName:消息发送到您的实例时InTSon,系统会在InTSon的方法表中查找方法,并找到我们调用的方法-[InTSon initWithName:]。该方法简化后如下所示:

// -[InTSon initWithName:]
- (id)initWithName:(NSString *)name {
    return [super initWithName:name];
}

这种方法可以[super initWithName:name]。因为它向 发送消息super,所以系统在 的类的超类的方法表中查找self。您的对象的类是InTSon,它的超类是InTParent。那么什么-[InTSon initWithName:]时候[super initWithName:name],系统在InTParent方法表中查找方法。它找到我们调用的方法-[InTParent initWithName:]。该方法简化后如下所示:

// -[InTParent initWithName:]
- (id)initWithName:(NSString *)name {
    return [self init];
}

这种方法可以[self init]。因为它向 发送消息self,所以系统在方法表中查找self的类。即使我们在-[InTParent initWithName:]self仍然是InTSon的一个实例。因此系统initInTSon的方法表中查找方法。它找到我们调用的方法-[InTSon init]。该方法简化后如下所示:

// -[InTSon init]
- (id)init {
    return [super initWithName:@"sdas"];
}

这个方法可以[super initWithName:],所以系统在超类的(InTParent's)方法表中查找并找到-[InTParent initWithName:]方法。正如我们刚刚看到的,-[InTParent initWithName:]最终调用[InTSon init]. 所以我们得到了无限递归,应用程序崩溃了。

要解决这个问题,您需要选择一个InTParentinit 方法作为其指定的初始化器您应该阅读 Apple 文档中有关指定初始化程序的信息。

您应该选择该initWithName:方法作为InTParent指定的初始化程序。此方法不得self. 它应该发送 super 指定的初始化消息(在 的情况下NSObjectinit消息),并且应该将它发送到super. 所以你需要编写这样的-[InTParent initWithName:]方法:

- (id)initWithName:(NSString *)name {
    // init is NSObject's designated initializer, and we send it to super.
    if ((self = [super init])) {
        // your initialization code here
    }
    return self;
}

通过选择一个指定的初始化器,并让您的指定初始化器只将超类的指定初始化器消息发送到super,您可以防止无限递归。

于 2012-09-07T03:16:17.960 回答
0

因为“self”是一个“InTSon”,所以[self init]调用了“InTSon”的init方法。

于 2012-09-07T02:54:16.690 回答