Swift学习—— initialization构造过程

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

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;
复制代码

实现方法以下:bash

在.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) {
        return nil;
    }
    _name = name;
    _age = age;
    return self;
}
复制代码

而Swif中的三种也是这样,init()以下。这里我在ide

.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这段代码时,有以下问题函数

  1. 若是没对存储属性name和age进行初始化,会报错,且须要在调用父类的构造方法super.init()方法以前须要对他俩赋值。
  2. 若是不带convenience,Xcode会提示报错,大意就是Designated initializer不能调self.init。而改为convenience initializer就对了。而后咱们点fix,init 前面就会自动插入 convenience。至于为何须要调用convenience,后面会作详细说明。

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

对存储属性初始化

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

init(name: String, age: Int) {
    self.name = name
    self.age = age

    super.init()
}
复制代码

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

  1. 初始化全部的存储属性。
  2. 它给每一个类一次机会,在新实例准备使用以前进一步自定义它们的存储型属性。

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

Designated Initializers

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

  1. 指定的构造器方法为最后一个,name和age都传入参数来初始化实例的那个。因此我理解的是最基础的init函数,别的init函数须要来调用的那个函数。
  2. 指定的构造器方法只能调用super.init(若是有父类的话),不能够调用self.init(平级的init函数)
  3. 固然,指定的构器并非必须只有一个,好比上面的根据name来生成实例的函数,若是也要写成指定构造器,代码以下:
init(name: String) {
    super.init()
    self.name = name
}
复制代码
  1. 不能写在extension扩展中。 若是初始化的时候,还须要调用别的方法,则这里也须要加上。即把方法 init(name: String, age: Int)里面的除了给age赋值代码外,其它代码都复制过来。

Convenience Initializers

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

  1. 不能调用父类(若是有父类)的指定构造方法,只能调用self.init(平级的init函数)。且最后确定要调到指定构造方法。这也是开头代码必须使用便利方法的主要缘由。
  2. 不像指定构造器,它能够写在extension扩展中。
  3. 在调用self.init以前不能访问self,或者依赖self的东西。
  4. 可被子类继承。可是子类必须重写父类的全部的指定构造方法。以上面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()的构造过程以下:

  1. Boy对象的内存分配,可是此时并无初始化。
  2. Boy.init()会调用从父类继承的convenience init方法。
  3. 而后根据代码调用到Person的convenience init(name: String) 方法。
  4. 调回到Boy重写的父类的init(name: String, age: Int)方法。
  5. 完成Boy类的全部存储属性的初始化。
  6. 调用父类Person的指定构造方法(而后完成Person类的全部存储属性的初始化,调用Person的父类的指定构造方法。会沿着类的继承链依次往上找,完成全部的存储属性的初始化,也就是上面说的初始化的第一阶段)
  7. 调用Person的从新赋值(若是有其它初始始化操做,也直接调用。而后调用Boy的print方法。也就是说第一阶段完成后,就进行了第二阶段,此阶段能够给存储属性从新赋值,也可调用其它方法来完成初始化,会依次从类的继承链顶端向下执行),而后return。
  8. Person的遍历方法返回。
相关文章
相关标签/搜索