“指定的”初始化方法
前面小节“初始化时类之间的协调”中的initWithName:方法就是B类的“指定的”初始化方法。所谓的“指定的”初始化方法就是那些能确保所有继承而来的实例变量都能被初始化的方法(通常都是向super发送消息来调用超类的初始化方法)。这种方法是进行初始化的核心,其他的初始化方法通常都会调用这种方法。在Cocoa编程中有这样的一个传统:“指定”的初始化方法通常是最能决定新对象特性的方法(通常情况下它和其他的初始化方法相比较而言参数最多,但也有例外)。重要的一点是需要确保继承而来的初始化方法必须也能正确地对派生类的对象进行初始化。这点通常都是通过重写(override)这些继承而来的初始化方法来实现的。例如,类C作为B的派生类,实现了一个初始化方法:initWithName:fromfile:。那么C类必须确保从B中继承的init和initWithName:这两个方法也能正确地对C类的对象进行初始化。此时,只要在C类中重写继承而来的这两个方法即可:
-initWithName:(char *)string
{
return [self initWithName:string fromFile:NULL];
}
在上面C类对initWithName:方法的重写中实际上是调用了initWithName:fromFile:这个初始化方法。
这样以来,对于C类的实例来说,用init初始化的时候,先调用的是C类的initWithName:,在该方法中调用initWithName:fromFile:,其调用关系如图3-3:
│
┌───────────────────────┐ │
┌─── │ -init │─────┘
│ │ -initWithName: │<────┐
class B │ │ │ │
│ │ │ │
│ │ │ │
│ └───────────────────────┘ │
│ │
│ │
│ │
│ ┌───────────────────────┐ │
└───>│ -initWithName: │ │
┌─── │ │ │
│ │ │ │
class C └──> │-initWithName:fromFile:│─────┘
│ │
│ │
└───────────────────────┘
图3-3
图3-3中忽略了一个很重要的细节。方法initWihtName:fromFile:作为C类的“指定的”初始化方法,其实现需要向super发送消息来完成B对象数据部分的初始化。但是B类中有两个进行初始化的方法,应该调用哪一个呢?这里说明一下不能调用B类的init方法的两个原因:● 如果调用B类的init方法将会导致方法调用的“死循环“。B类的init方法中调用了C的initWithName:,该方法中调用了initWithName:fromFile:,而在initWithName:fromFile:有调用了B类的init方法。从而形成初始化的”死循环“。
● 这样就没有复用到B类的initWihName:的方法。
原则:类中的“指定的”初始化方法中必须向super发送消息,从而调用其超类的“指定的”初始化方法。
指定的初始化方法是通过向super发送消息相互关联起来的,而其他的初始化方法则是通过向self发送消息来和这些“指定的”初始化方法关联起来的。
图3-4展示了类A,B和C的初始化方法之间的关系。图的左端显示的都是向self发送消息,右端显示的都是向super发送消息:
│
┌───────────────────────┐ │
│ -init │─────┘
│ │<────┐
class A │ │ │
│ │ │
│ │ │
└───────────────────────┘ │
│
│
│
┌───────────────────────┐ │
┌─── │ -init │─────┘
├──> │ -initWithName: │<────┐
class B │ │ │ │
│ │ │ │
│ │ │ │
│ └───────────────────────┘ │
│ │
│ │
│ │
│ ┌───────────────────────┐ │
└───>│ -initWithName: │ │
┌─── │ │ │
│ │ │ │
class C └──> │-initWithName:fromFile:│─────┘
│ │
│ │
└───────────────────────┘
图3-4
注意: B类的init方法中向self发送消息从而调用initWithName:方法。因此当init消息的接收者是B类的对象的时候,会调用B类的initWithName:方法,而当init消息的接收者是C类的对象的时候,则调用的是C类的initWithName:方法。