Objective-C初学: 对象的初始化

时间:2022-06-27 19:43:42

1. 关于new,alloc和init

对于创建对象来说,在C++中我们只要写好一个类:Class aClass;再 aClass anObject;就可以了。

但是,对于Obejctive-C来说,所有的对象都是动态对象,都是被分配在堆上的。因此不像上面方法创建的是自动对象变量,而是在创建之前就需要分配内存空间,因此就有了关于标题的方法。

Car *car = [[Car alloc] init];

这里将alloc(分配)和init(初始化)结合起来使用,这是非常重要的。因为由于ObjectiveC中存在类簇这一个概念,因此初始化返回的对象可能与分配的对象有所不同。因此,如果分开写成如下:

Car *car = [Car alloc];
[Car init];

则可能会导致car存在过两个不同类型的对象,这不是我们想看到的。

而对于new来说,其实就是这两个方法的重命名,但是它不方便我们用自己的初始化方法,如:

[[Car alloc] initWithTire];
// 此时用new则只会用init的方法进行初始化

2. 初始化方法

在初始化方法中,经常看到如下的形式:

if (self = [super init])
{
...
}

这是因为self参数是通过固定的距离(硬编码)寻址的,因此如果从init方法中返回了一个新对象,则需更新self
当初始化出现问题时,init方法可能会返回nil,这是一种典型的C语言风格。

重要: 一定要记得返回:return (self);且不容许出现可能未返回的情况!

2.1 指定初始化函数和便利初始化函数

在ObjectiveC的类中,初始化方法可以有很多,但是需要指定一个或N个初始化函数作为核心初始化函数,称为指定初始化函数,除了指定初始化函数函数外的初始化函数称为便利初始化函数,它是给你在初始化时提供更多便利的。在Swift里超类及超类和子类的指定初始化函数和便利初始化函数须遵循如下规则,反正以后肯定也要转到Swift
1. 每一个类必须拥有至少一个指定初始化函数,有些情况下子类通过继承继承了超类中的指定初始化函数,满足了这个条件。
2. 指定初始化函数必须调用其直接超类的指定构造器。
3. 便利构造器必须调用同一类中定义的其他构造器(”其他”构造器包括同一层级中的指定构造器便利构造器
4. 便利构造器必须最终以调用一个指定构造器结束。

快捷记忆
- 指定初始化函数必须总是向上代理
- 便利初始化函数必须总是横向代理

以图示来说明以上规则:
Objective-C初学: 对象的初始化


如上图所示:
超类中有一个指定初始化函数(满足条件1),还有两个便利初始化函数,层级链接,最终指向了指定初始化函数(满足条件3,4)。
在子类中,有两个指定初始化函数(满足条件1),并且都指向了超类的指定初始化函数(满足条件2)。及一个便利初始化函数指向了同一层级的指定初始化函数(满足条件3,4)。

再来个两层的:
Objective-C初学: 对象的初始化

可以看到各层类均满足以上四个条件,且通过图示我们可以看出,对于整条继承链,当层类需要利用指定构造器构造调用其超类的指定构造器,以完成整个继承类链的所有实例变量的初始化工作。

2.2 两段式构造过程

Swift中类的构造过程包含两个阶段:
- 第一阶段:
子类的便利构造器不工作(如果调用的是便利构造器的),会代理给当层的指定构造器,随后指定构造器会为该子类中所有的自己的实例变量进行初始化工作。随后,会调用其直接超类的指定构造器,如此延续,一直到最顶层。
Objective-C初学: 对象的初始化

  • 第二阶段:
    第一阶段完成后,从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例,最终,每层的便利构造器都可有机会进行实例变量的进一步定制。
    Objective-C初学: 对象的初始化

简单来说,就是先自下向上只初始化自己的实例变量,到顶以后再自顶向下进行进一步初始化工作,这样可以保证没吃呢过子类的实例变量都是最新的值。而不是被其超类的构造器所覆盖。

与ObjectiveC中的子类不同,Swift的子类不会默认继承父类的构造器,也就是说如果子类中有指定构造器,那么父类构造器必须要在子类中重载,这是为了方式一个父类的简单构造器被一个更复杂的子类继承,并被错误的用来创建子类的实例。


来个例子:
ObjectiveC基础教程第二版第十章修改例

Tire.h

#import <Cocoa/cocoa.h>

interface Tire: NSObject
{
double pressure;
double treadDepth;
}

- (id) initWithPressure: (double) pressure
treadDepth: (double) treadDepth;

- (void) setPressure: (double) pressure;

- (double) pressure;

- (void) setTreadDepth: (double) treadDepth;

- (double) treadDepth;

@end // Tire

Tire中第一个初始化函数是我们指定的指定构造器:

 (id) initWithPressure: (double) pressure
treadDepth: (double) treadDepth;

具体实现就不写了,这里面只有一个初始化函数,实现中还有一个init初始化函数如下:

- (id) init
{
if (self = [self initWithPressure: 34.0
treadDepth: 20.0])
{

}
return (self);
} // init

可以看到该init方法是便利构造器,调用的是上述的指定构造器。

现在写一个子类:AllWeatherTire如下:
AllWeatherTire.h

#import <Cocoa/cocoa.h>
#import "Tire.h"

@interface AllWeatherTire : Tire
{
double rainyHandling;
double snowHandling;
}

- (id) initWithPressure: (double) pressure
TreadDepth: (double) treadDepth
RainyHandling: (double) rainyHandling
snowHandling: (double) snowHandling;

- (id) initWithRainyHandling: (double) rainyHandling;

- (id) initWithSnowHandling: (double) snowHandling;

- (id) initWithRainyHandling: (double) RainyHandling
SnowHandling: (double) SnowHandling;

- (void) setRainyHandling: (double) rainyHandling;
- (double) rainyHandling;

- (void) setSnowHandling: (double) snowHandling;
- (double) snowHandling;

@end // AllWeatherTire

上面代码中,- (id) initWithPressure: TreadDepth: RAINYhandling: snowHandling方法即是子类的指定初始化函数。其他的都是便利构造器。

接下来我们看一下实现文件:
AllWeatherTire.m

#import "AllWeatherTire.h"

@implementation AllWeatherTire

- (id) init
{
if (self = [self initWithPressure: 34.0
TreadDepth: 20.0
RainyHandling: 23.7
snowHandling: 42.5])
{

}

return (self);
} // init

- (id) initWithPressure: (double) p
TreadDepth: (double) td
RainyHandling: (double) rh
snowHandling: (double) sh
{
if (self = [super initWithPressure: p treadDepth: td])
{
rainyHandling = rh;
snowHandling = sh;
}

return (self);
} // initWithPressure: TreadDepth: RainyHandling: SnowHangdling
// designated


- (id) initWithPressure: (double) p
TreadDepth: (double) td
{
if (self = [super initWithPressure: p treadDepth: td])
{
rainyHandling = 0;
snowHandling = 0;
}

return (self);
} // overload super class constructor


- (id) initWithRainyHandling: (double) rh
{
if (self = [self init])
{
rainyHandling = rh;
}

return (self);
} // initWithRainyHandling


- (id) initWithSnowHandling: (double) sh
{
if (self = [self init])
{
snowHandling = sh;
}

return (self);
} // initWithSnowHandling


- (id) initWithRainyHandling: (double) rh
SnowHandling: (double) sh
{
if (self = [self init])
{
rainyHandling = rh;
snowHandling = sh;
}

return (self);
} // initWithSnowHandLing: SnowHandling


- (void) setRainyHandling: (double) rh
{
rainyHandling = rh;
} // setRainyHandling


- (double) rainyHandling
{
return rainyHandling;
} // rainyHandling


- (void) setSnowHandling: (double) sh
{
snowHandling = sh;
} // setSnowHandling


- (double) snowHandling
{
return snowHandling;
} // snowHandling


- (NSString *) description
{
NSString *desc;
desc = [[NSString alloc] initWithFormat:
@"AllWeatherRadial: %.1f / %.1f / %.1f / %.1f",
[self pressure], [self treadDepth],
[self rainyHandling],
[self snowHandling]];

return (desc);

} // description

@end // AllWeatherTire

好大一段,整理一下:
父类Tire类中的指定构造函数是:
(id) initWithPressure: (double) pressure
treadDepth: (double) treadDepth;

无便利构造器。

子类AllWeatherTire类中的指定构造函数是:
- (id) initWithPressure: (double) pressure
TreadDepth: (double) treadDepth
RainyHandling: (double) rainyHandling
snowHandling: (double) snowHandling;

便利构造函数如下:
- (id) initWithRainyHandling: (double) rainyHandling;

- (id) initWithSnowHandling: (double) snowHandling;

- (id) initWithRainyHandling: (double) RainyHandling
SnowHandling: (double) SnowHandling;

以及父类子类都有一个init的便利初始化方法,这里就不写了。

在AllWeatherTire.m文件中,可以看到各类的层级关系,如下图所示:

Objective-C初学: 对象的初始化

Resource Reference

http://cnbin.github.io/blog/2016/02/24/lei-de-ji-cheng-he-gou-zao-guo-cheng-zhi-ding-gou-zao-qi-he-bian-li-gou-zao-qi/
Objective-C基础教程第二版