关于父对象的init方法
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(扩展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中,也许这样:Son initWithName方法 – > Parent initWithName方法 – > Parent init方法
请帮帮我!
为了理解这个行为,你必须了解Objective-C消息调度的工作原理。 这是一个很好的例子来说明这一点。
在高层次上,无论何时您调用任何对象上的方法,Objective-C运行时都会查找提供了派生最多(类层次结构中最深的)类的实现。 如果它没有find它,它将会进入下一个派生的,等等,直到NSObject
。 它第一次发现一个匹配select器(方法名称,大致)的实现,它将执行该实现。 当你调用super
,你正在指定发送一个消息给下一个派生类的实现。
因此,在你的代码中,你调用InTSon
类的alloc
,它返回IntSon
一个实例,将isa
指针设置为类对象InTSon
。 isa
指针是在升序类层次结构的过程中如何查找方法实现的。
所以在你有一个InTSon
实例之后,你可以调用initWithName:
InTSon
然后它检查isa
指向的类( InTSon
是这个方法的一个实现,它发现它,执行它,导致你的第一个输出:
"Son initWithName method"
之后,立即调用该方法的超类实现,该方法在InTParent
中查找initWithName:
实现initWithName:
并执行该代码,从而生成第二个输出:
Parent initWithName method
现在,在这里你看到了一个偏离Java的地方 – 你自己调用init
。 但是, self
是指向InTSon
实例的指针。 所以当运行时parsing这个消息的时候,它首先在InTSon
类中实现init
。 当然,它find它,并执行该方法的代码,它给你你的第三个输出, Son init method
。 接下来调用super,它查找并执行init
的InTParent
实现,并为您提供最终的输出。
总而言之,无论类层次结构中的哪个方法被调用,如果它是在self
上调用的,它将始终执行该方法的派生最多的实现。 希望这有助于,如果您有任何问题,请让我知道!
你的类图如下所示:
将initWithName:
消息发送给InTSon
的实例时,系统将在InTSon
的方法表中查找该方法,并find我们调用的方法-[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
方法。 它find我们调用的方法-[InTParent initWithName:]
。 该方法简化,如下所示:
// -[InTParent initWithName:] - (id)initWithName:(NSString *)name { return [self init]; }
这个方法做[self init]
。 因为它将消息发送给self
,系统会在方法表中查找self
的类。 即使我们在-[InTParent initWithName:]
, self
仍然是InTSon
类的一个实例 。 所以系统在InTSon
的方法表中InTSon
init
方法。 它find了我们所称的方法-[InTSon init]
。 该方法简化,如下所示:
// -[InTSon init] - (id)init { return [super initWithName:@"sdas"]; }
此方法执行[super initWithName:]
,因此系统查找超类的( InTParent
's)方法表并查找-[InTParent initWithName:]
方法。 正如我们刚刚看到的, -[InTParent initWithName:]
最终调用[InTSon init]
。 所以我们得到无限的recursion和应用程序崩溃。
为了解决这个问题,你需要select一个InTParent
的init方法作为它的指定初始化方法。 你应该阅读苹果文档中指定的初始值设定项。
您应该selectinitWithName:
方法作为InTParent
的指定初始值设定项。 这个方法不能发送任何init消息给self
。 它应该只发送超级指定的初始化消息(在NSObject
的情况下是init
消息),它应该发送给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; }
通过select指定的初始值设定项,并使指定的初始值设定项仅super
类的指定初始值设定项消息发送给super
,可防止无限recursion。
因为“self”是“InTSon”,所以[self init]调用“InTSon”初始化方法。