Swift

Swift 5.x 类的继承和初始化

2020-07-04  本文已影响0人  ShenYj

注: 最开始接触Swift 2.x的时候, 那会我对便捷初始化的称呼是: 便利构造函数, 其实一个意思

指定初始化器和便捷初始化器

指定初始化器语法

init(parameters) {
    statements
}

便捷初始化器语法

convenience init(parameters) {
    statements
}
类的初始化委托
类的初始化委托.png

两段式初始化
  • 指定或便捷初始化器在类中被调用
  • 为这个类的新实例分配内存. 内存还没有被初始化
  • 这个类的指定初始化器确保所有由此类引入的存储属性都有一个值. 现在这些存储属性的内存被初始化
  • 指定初始化器上交父类的初始化器为其存储属性执行相同的任务
  • 这个调用父类初始化器的过程将沿着初始化器链一直向上进行, 知道到达初始化器链的最顶部
  • 一旦到达了初始化器链的最顶部, 在链顶部的类确保所有的存储属性都有一个值, 此实例的内存被认为完全初始化了, 此时第一阶段完成
  • 从顶部初始化器往下, 链中的每一个指定初始化器都有机会进一步定制示例. 初始化器现在能够访问self并且可以修改它的属性, 调用它的实例方法等等
  • 最终, 链中任何便捷初始化器都有机会定制实例以及使用self, 此时第二阶段完成
安全检查
  1. 指定初始化器必须保证在向上委托给父类初始化器之前, 其所在类引入的所有属性都要初始化完成.
  2. 指定初始化器必须向上委托父类初始化器, 然后才能为继承的属性设置新值. 如果不这样做, 指定初始化器富裕的新值将被父类中的初始化器锁覆盖.
  3. 便捷初始化器必须先委托同类中的其它初始化器, 然后再为任意属性赋新值(包括同类里定义的属性). 如果没这么做, 便捷初始化器赋予的新值将被自己类中其它指定初始化器所覆盖.
  4. 初始化器在第一阶段初始化完成之前, 不能调用任何实例方法、不能读取任何实例属性的值, 也不能引用self作为值.

e.g. 安全检查
class Person {
    var name: String
    var age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    convenience init() {
        self.init(name: "unKonw", age: 0)
    }
}

class Teacher: Person {
    var salary: Int
    init(name: String, age: Int, salary: Int) {
        super.init(name: name, age: age)
        self.salary = salary
        self.name = name + "老师"
    }
    
    convenience init(name: String) {
        self.init(name: name, age: 30, salary: 5000)
    }
    
    func showInfo() -> Void {
        print("name: \(name) age: \(age) salary: \(salary)")
    }
}

这个示例违背了第1条安全检查, 错误代码:

error1.png
Teacher类中, salary是该类下新增属性, 在调用super初始化器前, 并未完成salary属性的初始化
所以需要将salary属性初始化放在调用super之前

正确的代码:

  init(name: String, age: Int, salary: Int) {
        self.salary = salary
        super.init(name: name, age: age)
  }
error2.png
其余代码部分一致, 只是将name属性初始化的位置进行了调整, nameTeacher父类的属性, 这种写法违背了安全检查第2条, 父类的属性需要在调用父类的初始化器后才能赋予新值, 所以正确写法是:
  init(name: String, age: Int, salary: Int) {
        self.salary = salary
        super.init(name: name, age: age)
        self.name = name + "老师"
    }
error3.png
Teacher的便捷初始化器下赋予name属性新值, 因为name是父类属性, 同样需要在调用父类的初始化器后才能赋予新值, 而又因为是在当前类的便利初始化器中, 需要通过调用自身的指定初始化器完成初始化
所以正确的写法是:
  convenience init(name: String) {
        self.init(name: name, age: 30, salary: 5000)
        self.name = name + "老师"
    }
error4.png
第4条, 在初始化完成前调用了实例方法
正确方式:
  init(name: String, age: Int, salary: Int) {
        self.salary = salary
        super.init(name: name, age: age)
        self.name = name + "老师"
        self.showInfo()
    }
初始化器的继承和重写
初始化器的自动继承
可失败初始化器
必要初始化器
反初始化
    deinit {
        
    }
上一篇 下一篇

猜你喜欢

热点阅读