Swift学习—— initialization构造过程

原标题:Swift学习—— initialization构造过程

作者丨小小甜

链接:

https://juejin.im/post/5dce8cdee51d45400020c978

在Swift中对象的初始化方法分为两种,指定构造器(Designated Initializers)和便利构造器(Convenience Initializers)。记录一下我对它的初步理解。

Swift构造过程

其实就是OC的init初始化方法。OC中一般初始化某个类(以Person为例,属性有name和age),使用以init开头的方法,如下:

在.h中暴露出声明方法。

//默认name="",age=0

- (instancetype)init;

//以name来初始化,默认age=0

- (instancetype)initWithName:(NSString *)name;

//赋值name和age来初始化

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;

实现方法如下:

在.m中写方法实现。

// 便利构造器

- (instancetype)init {

return[self initWithName:@""];

}

// 便利构造器

- (instancetype)initWithName:(NSString *)name {

return[self initWithName:name age:0];

}

// 指定构造器

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {

self = [super init];

if(!self) {

returnnil;

}

_name = name;

_age = age;

returnself;

}

而Swif中的三种也是这样,init如下。这里我在

.swift中写方法

//此处,我给name和age初始值。

var name: String = ""

var age: Int = 0

// 便利构造器

override convenience init{

self.init(name: "haha")//此处默认名字是haha,是为了方便看出子类可继承convenience init方法。

}

// 便利构造器

convenience init(name: String) {

self.init(name: name, age:0)

}

// 指定构造器

init(name: String, age: Int) {

super.init

self.name = name

self.age = age

}

写Swift这段代码时,有如下问题

如果没对存储属性name和age进行初始化,会报错,且需要在调用父类的构造方法super.init方法之前需要对他俩赋值。

如果不带convenience,Xcode会提示报错,大意就是Designated initializer不能调self.init。而改成convenience initializer就对了。然后我们点fix,init 前面就会自动插入 convenience。至于为什么需要调用convenience,后面会做详细说明。

注意:Swift不同于OC的init,它没有返回值。在这一过程里面初始所有的存储属性和其它的必要初始化操作。

注意:Swift不同于OC的init,它没有返回值。在这一过程里面初始所有的存储属性和其它的必要初始化操作。

存储属性,在调用任何方法(比如子类调父类的super.init,或者调本类的方法)之前,都应该确保在本类对存储属性进行初始化。如果name和age并没有给初始值,则代码应该改成如下,以init(name: String, age: Int)为例:

init(name: String, age: Int) {

self.name = name

self.age = age

super.init

}

这是由于Swift的内存安全,在调用父类构造方法之前,确保对本类的所有存储属性进行初始化。采用了Two-Phase Initialization(两段式构造)模型来实现内存安全。

初始化所有的存储属性。

它给每个类一次机会,在新实例准备使用之前进一步自定义它们的存储型属性。

Github源码文档有一处是三段式构造,第三段:如果还有其它操作,可执行其它方法来完成初始化。

Github源码文档有一处是三段式构造,第三段:如果还有其它操作,可执行其它方法来完成初始化。

指定构造器,这是一个类最主要的方法,如果有父类,则子类的指定构造器方法必须要调父类的指定构造方法与父类关联。比如上面的Swift的Person类,

指定的构造器方法为最后一个,name和age都传入参数来初始化实例的那个。所以我理解的是最基础的init函数,别的init函数需要来调用的那个函数。

指定的构造器方法只能调用super.init(如果有父类的话),不可以调用self.init(平级的init函数)

当然,指定的构器并不是必须只有一个,比如上面的根据name来生成实例的函数,如果也要写成指定构造器,代码如下:

init(name: String) {

super.init

self.name = name

}

不能写在extension扩展中。如果初始化的时候,还需要调用别的方法,则这里也需要加上。即把方法 init(name: String, age: Int)里面的除了给age赋值代码外,其它代码都复制过来。

便利构造器,便捷生成实例的方法,对指定构造器的参数做了选择,可不用传那么多参数。

不能调用父类(如果有父类)的指定构造方法,只能调用self.init(平级的init函数)。且最后肯定要调到指定构造方法。这也是开头代码必须使用便利方法的主要原因。

不像指定构造器,它可以写在extension扩展中。

在调用self.init之前不能访问self,或者依赖self的东西。

可被子类继承。但是子类必须重写父类的所有的指定构造方法。以上面Person类为例,我又创建了一个它的子类Boy继承Person。如果想用Boy.init方法,则Boy类里应该重写Person类的所有的指定构造方法。代码如下:

class Boy: Person {

var girlFriendName: String

override init(name: String, age: Int) {

self.girlFriendName = "girl"

super.init(name: name, age: age)

print("我完成了init")

}

}

如果Person里面还有另一个指定构造方法,则Boy类里面也需要重写此方法。否则调用Boy.init会报错。调用Boy.init的构造过程如下:

Boy对象的内存分配,但是此时并没有初始化。

Boy.init会调用从父类继承的convenience init方法。

然后根据代码调用到Person的convenience init(name: String) 方法。

调回到Boy重写的父类的init(name: String, age: Int)方法。

完成Boy类的所有存储属性的初始化。

调用父类Person的指定构造方法(然后完成Person类的所有存储属性的初始化,调用Person的父类的指定构造方法。会沿着类的继承链依次往上找,完成所有的存储属性的初始化,也就是上面说的初始化的第一阶段)

调用Person的重新赋值(如果有其它初始始化操作,也直接调用。然后调用Boy的print方法。也就是说第一阶段完成后,就进行了第二阶段,此阶段可以给存储属性重新赋值,也可调用其它方法来完成初始化,会依次从类的继承链顶端向下执行),然后return。

返回搜狐,查看更多

责任编辑:

平台声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
阅读 ()
推荐阅读