我制作了一个测试应用,以了解init方法的确切工作原理。在我简单的UIViewController
中,我称之为:
- (id)init {
self = [super init];
self.propertyArray = [NSArray new];
NSLog(@"init called");
return self;
}
以上内容不会在NSLog中打印任何值。但是,当我写:
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
NSLog(@"init called");
self.propertyArray = [NSArray new];
return self;
}
它打印" init叫"在控制台。所以我的问题是:为什么调用init方法而另一个不调用?当我想在视图加载(以及任何其他方法调用)之前做我的东西时,我必须使用哪一个?
任何解释都将不胜感激,谢谢。
答案 0 :(得分:2)
首先,您在问题中提到ViewController。 UIViewController的指定初始化程序是initWithNibName:bundle:
你永远不想在UIViewController上只覆盖init。
每个对象都有一个生命周期:
在代码中初始化时,您有指定的初始化程序。您可以在该课程的文档中找到它。对于NSObject派生类,这将是init:
- (id)init
{
self = [super init];
if (self) {
// perform initialization code here
}
return self;
}
使用NSKeyUnrchiving反序列化的所有对象(在Storyboard或NIB(XIB)的情况下发生的情况)都会被解码。此过程使用initWithCoder初始化程序,并在取消归档过程中发生:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
// perform initialization code here
}
return self;
}
由于这个生命周期,创建一个从每个初始化程序调用的共享初始化程序是很常见的:
- (void)sharedInit
{
// do init stuff here
}
- (id)init
{
self = [super init];
if (self) {
[self sharedInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self sharedInit];
}
return self;
}
更进一步。对于Storyboard和XIB,如果要在完成取消归档后执行初始化或更新并保证所有出口和操作都已连接,则应使用awakeFromNib:
- (void)awakeFromNib
{
// do init or other stuff to be done after class is loaded from Interface Builder
}
答案 1 :(得分:1)
在代码中实例化类时,根据需要选择要调用的初始化程序。当通过框架代码实例化类时,您需要查阅文档以找出将调用的初始化程序。
您看到所描述的行为的原因是您的视图控制器位于故事板中。根据Cocoa文档,当通过故事板实例化视图控制器时,会调用其initWithCoder:
初始值设定项。通常,此调用会执行when an object gets deserialized。
请注意,通常会检查self = [super initWithCoder:aDecoder];
分配的结果,并在self
设置为nil
时跳过进一步初始化。
答案 2 :(得分:1)
当您从nib文件(和故事板)加载视图控制器时,它使用initWithCoder:
所以在您的示例中这就是它调用此方法的原因。
如果您以编程方式创建视图控制器,则此方法无法正常工作,您应该覆盖initWithFrame:
初始化程序,还应通过调用创建视图控制器
[[UIViewController alloc] initWithFrame:...];
答案 3 :(得分:1)
不同的init
是不同的构造函数。与任何其他语言一样,实例由最合适的构造函数实例化。从存档中恢复时,这是initWithCoder:
。
作为样式点,请注意在构造函数中使用self.propertyArray
被视为错误形式。考虑如果子类覆盖setPropertyArray:
会发生什么。您将对未完全实例化的对象进行方法调用。相反,您应该直接访问实例变量,并执行惯用if(self)
检查以确保安全。