swift 类、结构体和枚举的初始化(二)

这是我参与更文挑战的第16天,活动详情查看: 更文挑战

类的初始化过程

  • 类里面的所有存储型属性,包括所有继承自父类的属性,都必须在初始化过程中设置初始值
  • Swift 为类类型提供了两种初始化来确保实例中所有存储型属性都能获得初始值,它们分别是指定初始化和便利初始化

指定初始化

每个类至少有一个指定初始化方法,必须实现所有存储属性的初始化,并根据父类链往上调用父类的初始化来实现父类的初始化

class Student {
    var name:String
    init(name:String) {
        self.name = name
    }
}
复制代码

便利初始化(在init关键字之前放置convenience关键字)

可以定义便利初始化方法来调用同一个类中的指定初始化方法,并为其参数提供默认值

class People: NSObject {
    var name:String
    init(name:String) {
        self.name = name
    }
    convenience override init(){
        self.init(name: "xiaoming")
    }
}
复制代码

类的初始化代理规则(类的指定初始化方法和便利初始化方法的相互调用规则)

  • 指定初始化必须调用其父类的指定初始化(指定初始化只能调用指定初始化)
  • 便利初始化必须调用同类中定义的其他初始化
  • 便利初始化必须最终导致一个指定初始化被调用

初始化.png

class Student {
    var age : Int
    init(){
        age = 10
    }
    convenience init(a:Int){
        self.init()
        age = a
        print(self)  // 打印两次, 第一次是Student, 第二次是Boy
    }
}

class Boy : Student {
    var name: String
    override init(){
        name = "boy"
        super.init()
    }
}

var stu = Student(a: 2)
var boy = Boy(a: 2)
复制代码

两段式构造过程

  • 一个对象的内存只有在其所有存储型属性确定之后才能完全初始化
  • 类初始化有两阶段

第一阶段,在类中的每个存储属性分配一个初始值
第二阶段,每个类的实例在被使用之前进一步定义其存储的属性

第一阶段
• 某个指定初始化或便利初始化被调用
• 完成新实例内存的分配,但此时内存还没有被初始化
• 指定初始化确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化
• 指定初始化将调用父类的初始化,完成父类属性的初始化
• 这个调用父类初始化的过程沿着初始化链一直往上执行,直到到达初始化链的最顶部
• 当到达了初始化链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化
复制代码
第二阶段
• 从顶部初始化链一直往下,每个初始化链中类的指定初始化都有机会进一步定制实例。初始化此时可以访问self修改它的属性并调用实例方法等等
• 最终,任意初始化初始化链中的便利初始化可以有机会定制实例和使用self
复制代码
class Student {
    var type:String
    init() {
        type = "学生"
    }
}

class Boy: Student {
    //第一阶段:初始化存储属性
    var age = 0
    override init() {
        //第一阶段:初始化父类
        super.init()
        //第二阶段:子类自定义
        age = 10
    }
}
let s = Boy()
print(s.type) //学生
print(s.age)  //10
复制代码
当你调用Boy()时
1.首先会调用super.init()初始化父类,这时Student类的属性在自身的指定初始化初始化中被初始化完成
2.一旦父类初始化完成,就可以初始化子类的属性,并且可以子类定制属性,这里个性化设置age =10
复制代码
  • Swift的编译器执行四个有用的安全检查,以确保完成两阶段初始化而不会出现错误:

1.指定初始化必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的初始化。
2.指定初始化必须先调用父类初始化,然后再为继承的属性设置新值。如果没这么做,指定初始化赋予的新值将被父类中的初始化所覆盖。
3.便利初始化必须先调用同一类中的其它初始化,然后再为任意属性赋新值。如果没这么做,便利初始化赋予的新值将被同一类中其它指定初始化所覆盖。
4.初始化在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引用self作为一个值

初始化的继承和重写

  • 跟 OC 中的子类不同,Swift 中的子类默认情况下不会继承父类的初始化,因为Swift不像OC会给属性默认值。Swift 的这种机制可以防止一个父类的简单初始化被一个更精细的子类继承,并被错误地用来创建子类的实例
  • 如果子类的指派初始化和父类相同, 也要用override来修饰. 但是, 如果子类的指派初始化与父类的便利初始化相同, 那么父类的便利初始化永远都不会被子类调用到, 所以这种情况是不需要写override的.

初始化的自动继承

  • 子类在默认情况下不会继承父类的初始化,但是如果满足特定条件就可以
    • 如果子类没有定义任何指定初始化,它将自动继承所有父类的指定初始化
    • 子类提供了全部的父类指定初始化而不是从情况1获得的, 即使是提供了一部分实现, 那么它将自动继承所有的父类便利初始化. (个人认为, 调用实现的那一个指派初始化初始化的便利初始化都可以, 其余的不行可以更智能, 而且也不会出问题)
class ClassA {
    init(a:Int){
    }
    
    init(b:Float) {
    }
    
    convenience init(c:Int){
    }
    
    convenience init(d:Float){
    }
}

class ClassB: ClassA {
    override init(a: Int) {
        super.init(a: a)
    }
    override init(b: Float) {
        super.init(b: b)
    }
}
复制代码

ClassB 中实现了 ClassA 中所有的指定初始化,那么在初始化 ClassB 对象的时候,选择方法如下
所有的指定初始化.png

class ClassB: ClassA {
    override init(a: Int) {
        super.init(a: a)
    }
}
复制代码

ClassB 中实现了 ClassA 中个别的指定初始化,那么在初始化 ClassB 对象的时候,选择方法如下
个别的指定初始化.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享