15.构造过程

2021-07-19  本文已影响0人  LucXion

存储属性初始化

为存储型属性分配默认值或者在构造器中设置初始值时,它们的值是被直接设置的,不会触发任何属性观察者。

形参命名和实参标签

构造器并不像函数和方法那样在括号前有一个可辨别的方法名。因此在调用构造器时,主要通过构造器中形参命名和类型来确定应该被调用的构造器。正因如此,如果你在定义构造器时没有提供实参标签,Swift 会为构造器的每个形参自动生成一个实参标签。

struct Person {
    var name:String
    var age:Int
    // 没有实参标签,有两个形参name、age
    init(_ name:String,_ age:Int){
        self.name = name
        self.age = age
    }
}

常量属性

对于类的实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。

结构体的逐一成员构造器

struct Person {
    var name:String
    var age:Int = 12
    /*
     自动生成两个逐一成员构造器
     init(name:)// 因为age已经设置了默认值
     init(name:age:)//如果age是常量,那么就不会自动生成这个构造器
     */
}

指定构造器和便利构造器

指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并调用合适的父类构造器让构造过程沿着父类链继续往上进行。类倾向于拥有极少的指定构造器,但每个类都必须至少拥有一个指定构造器。

便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为部分形参提供默认值。你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清晰明了。

class Person {
    var name:String = ""
    var age:Int = 1
    init(parama:String){
        // 指定构造器
    }
    convenience init(data:String){ // 便利构造器 convenience
        self.init(parama: data)// 便利构造器内必须调用指定构造器
    }
}

继承关系下的指定构造器和便利构造器


两段式构造过程

Swift 中类的构造过程包含两个阶段。第一个阶段,类中的每个存储型属性赋一个初始值。当每个存储型属性的初始值被赋值后,第二阶段开始,它给每个类一次机会,在新实例准备使用之前进一步自定义它们的存储型属性。

两段式构造过程的使用让构造过程更安全,同时在整个类层级结构中给予了每个类完全的灵活性。两段式构造过程可以防止属性值在初始化之前被访问,也可以防止属性被另外一个构造器意外地赋予不同的值。

阶段 1

阶段 2

总结:
构造器在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引用 self 作为一个值。

指定构造器:优先保证当前类内声明的属性被赋值(第一阶段) -> 调用父类的指定构造器(第一阶段)-> 给父类的属性赋新值(第二阶段)

便利构造器:调用其他构造器(第一阶段) -> 赋新值(第二阶段)

构造器的继承和重写

Swift 中的子类默认情况下不会继承父类的构造器

如果子类的构造器没有在阶段 2 过程中做自定义操作,并且父类有一个无参数的指定构造器,你可以在所有子类的存储属性赋值之后省略 super.init() 的调用。

重写的不能是父类的便利构造方法

class Food {
    var name: String
    init(name: String) {  // 指定构造器
        self.name = name
    }
    convenience init() { // 便利构造器 convenience
        self.init(name: "[Unnamed]")// 便利构造器内必须调用指定构造器
    }
}
// 子类中没有新增的未初始化属性,如果没有指定构造器,默认继承父类的指定构造器
class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
        self.quantity = quantity
        super.init(name: name)
    }
    override convenience init(name: String) {//便利构造器,
        self.init(name: name, quantity: 1)
    }
}

let oneMysteryItem = RecipeIngredient()// 调用继承自父类的无参便利构造器 -> 子类便利构造器(重写的父类指定构造器) -> 子类指定构造器 -> 父类指定构造器

可失败的构造器

class Person {
    var name:String
    init(){
        print("3")
        self.name = "[unName]"
    }
    init?(nameCannotBeEmpty name:String){
        print("2")
        if name.isEmpty {
            return nil
        }
        self.name = name
    }
}

class Student:Person {
    var id : Int = 0
    init?(id:Int,name:String){
        print("1")
        if id <= 0 {
            return nil
        }
        self.id = id
//          super.init(nameCannotBeEmpty: name)  可以调用父类指定构造器,也可以调用父类的可失败构造器
        super.init()
    }
}
var s = Student.init(id: 10086, name: "Li")
print(s?.name,s?.id)

必要构造器

在类的构造器前添加 required 修饰符表明所有该类的子类都必须实现该构造器

通过闭包或者全局函数给存储属性设置默认值

如果你使用闭包来初始化属性,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能在闭包里访问其它属性,即使这些属性有默认值。同样,你也不能使用隐式的 self 属性,或者调用任何实例方法。

上一篇 下一篇

猜你喜欢

热点阅读