构造过程

207 阅读9分钟
  1. 定义属性的三种方式
    1. 直接赋初始值
    2. 只写类型,在构造器中赋初始值
    3. 当前是缺省值,以后一定有值。或者当前是缺省值,以后可能还是缺省值。
class Test {
    //给属性赋默认值
    var a = 0
    //给属性类型,默认值在构造器中赋值
    var b: Int
    //当前是缺省值,
    var c: Int?//当前是缺省值,以后可能还是缺省值
    var d: Int!//当前是缺省值,以后一定有值
    
    init(b: Int) {
        self.b = b
    }
}
  1. 构造器简单使用
struct Color {
    var red: Double, green: Double, blue: Double
    
    init(red: Double, green: Double, blue: Double) {
        self.red = red;
        self.green = green
        self.blue = blue
    }
    
    init(white: Double) {
        self.red = white
        self.green = white
        self.blue = white
    }
}

var aColor = Color(red: 1.0, green: 0, blue: 0)
var bColor = Color(white: 0.5)
print("aColor: \(aColor)")
print("bColor: \(bColor)")
  1. 结构体的逐一成员构造器
    1. 结构体和类都有一个默认的无参数的构造器。
    2. 结构体比类多了一个默认的逐一成员构造器。
    3. 结构体的逐一成员构造器调用时候可以省略有默认值的参数。(swift5.1最新版本更新的内容)
struct Test {
    var a = 3
    var b = 5
}

var a = Test(a: 3, b: 6)
var b = Test(a: 5)
print(a)
print(b)
  1. 当我们在类或者结构体中写了自定义的构造器后,默认的构造器将消失。

  2. 类的构造器构造过程
    构造过程分为两个阶段

    阶段一:

    1. 首先便利构造器调用本类的指定构造器。
    2. 本类的指定构造器先初始化本类的属性,之后再调用父类的指定构造器。
    3. 父类的指定构造器再次初始化自身的属性,之后再次调用它的父类的指定构造器。
    4. 这样就指定构造器就沿着它的继承链向上代理,直到最顶部。这时候完成了整个类的初始化。

    阶段二:

    1. 当指定构造器向上代理到最顶部时候,接下来要向下代理,进行第二阶段
    2. 接下来沿着指定构造器的继承链向下执行自定义部分(可能没有)
    3. 最后到达了遍历构造器处,这时候已经完全初始化了本类的所有属性。之后就可以使用这些属性了。
class Test {
    var a: Int
    
    init(a: Int) {
        print("Test-init-start")
        self.a = a
        print("Test-init-end")
    }
}
class Test1: Test {
    var b: Int
    
    init(b: Int, a: Int) {
        print("Test1-init-start")
        self.b = b
        super.init(a: a)
        print("Test1-init-end")
    }
}


class Test2: Test1 {
    var c: Int
     init(c:Int, b: Int, a: Int) {
        print("Test2-init-start")
        self.c = c
        super.init(b: b, a: a)
        print("Test2-init-end")
    }
    
     convenience init(){
        //阶段一与阶段二:利用指定构造器完成整个类的属性初始化
        print("阶段一:调用指定构造器沿着继承链向上完成属性的初始化   阶段二:在从链顶向下完成自定义部分")
        print("convenience init")
        self.init(c: 0, b: 0, a: 0)
        
        //最后到达遍历构造器,这时候可以完全访问属性了
        let d = (a + 1) * (b + 2) * (c + 3)
        print(d)
    }
}

//var test = Test2(c: 1, b: 2, a: 3)
var test1 = Test2()

//打印如下:
//阶段一:调用指定构造器沿着继承链向上完成属性的初始化   阶段二:在从链顶向下完成自定义部分
//convenience init
//Test2-init-start
//Test1-init-start
//Test-init-start
//Test-init-end
//Test1-init-end
//Test2-init-end
//6
  1. 结构体中的构造器

    1. 结构体中只有指定构造器,且没有继承链。
    2. 类中有指定构造器,还有遍历构造器,还有继承链。相对更复杂些。
  2. 构造器的继承和重写

    1. 如果子类不写任何指定构造器(包括自定义的和父类的),这时候子类将继承所有父类的构造器(包括父类的指定构造器和父类的便利构造器)
    2. 如果子类把父类的所有指定构造器都重写了,那么这时候子类将会继承父类的所有构造器,主要是父类的遍历构造器。
    3. 如果子类继承了父类的所有构造器,那么这个时候子类可以选择性的重写父类的遍历构造器,并且前边不用加关键在override。
  3. 构造器便利构造器的使用

class Food {
    var name: String
    
    init(name:String) {
        self.name = name
    }
    
    convenience init(){
        self.init(name: "Unnamed")
    }
}

class Material: Food {
    var count: Int
    
    init(count: Int, name: String) {
        self.count = count
        super.init(name: name)
    }
    
    convenience override init(name: String) {
        self.init(count:1, name:name)
    }
}

class ShoppingListItem: Material {
    var purchased = false
    func description() {
        let purchas = purchased ? " ✔" : " ✘"
        print("\(count) X \(name) \(purchas)")
    }
}

let listItem = [ShoppingListItem(), ShoppingListItem(name: "面包"), ShoppingListItem(count: 5, name: "巧克力")]
listItem[1].purchased = true
listItem[0].description()
listItem[1].description()
listItem[2].description()
  1. 可失败构造器
    1. 当在给一个构造器传入非法入参时候,这时候没办法给属性赋值,这时候就可以用可失败构造器。
    2. 在可失败构造器内部判断非法入参,返回nil就是构造失败了。
struct Animal {
    let species: String
    init?(species: String){
        if species.isEmpty {
            return nil
        }
        self.species = species
    }
}

var a = Animal(species: "")
if a == nil{
    print("构造失败")
}else{
    print("构造成功:" + a!.species)
}
//打印如下
//构造失败
  1. 枚举类型下的可失败构造器
enum Direction: Character {
    case D = "D", X = "X", N = "N", B = "B"

    init?(direction: Character){
        switch direction {
        case "D":
            self = .D
        case "X":
            self = .X
        case "N":
            self = .N
        case "B":
            self = .B
        default:
            return nil
        }
    }
}

var a = Direction(direction: "D")
print(a!.rawValue)

if let a = Direction(direction: "T") {
    print("可选枚举构造成功:\(a.rawValue)")
}else{
    print("可选枚举构造失败")
}
  1. 原始值枚举类型构造器是一个可失败的构造器
enum Direction: Character {
    case D = "D", X = "X", N = "N", B = "B"
}

if let a = Direction(rawValue: "T") {
    print("可选枚举构造成功:\(a.rawValue)")
}else{
    print("可选枚举构造失败")
}
  1. 构造失败向上代理
class Product {
    var name: String
    init?(name: String) {
        if name.isEmpty {
            return nil
        }
        self.name = name
    }
}

class CartItem: Product {
    var count: Int
    init?(name: String, count: Int) {
        if count < 1 {
            return nil
        }
        self.count = count
        
        super.init(name: name)
    }
}

if let a = CartItem(name: "2", count: 10){
    print("构造成功: \(a.name) \(a.count)")
}else{
    print("构造失败")
}
  1. 可失败构造器的继承和重写
    1. 不可失败构造器可以重写父类的不可失败构造器。
    2. 可失败构造器可以重写父类的可失败构造器。
    3. 不可失败构造器可以重写父类的可失败构造器,这时候往往在内部要调用父类的不可失构造器。如果调用那个被重写的可失败构造器,那么就要强制解包,这有可能会发生运行时错误。
    4. 可失败构造器不能够重写父类的不可失败构造器。
class Product {
    var name: String?
    init() {}
    
    init?(name: String) {
        if name.isEmpty {
            return nil
        }
        self.name = name
    }
}

class CartItem: Product {
    override init() {
        super.init()
        self.name = "Unnamed"
    }
    
    override init(name: String) {
        super.init()
        if self.name == nil {
            self.name = "Unnamed"
        }
    }
}

var a = CartItem(name: "")
print(a.name!)
  1. 必要构造器
    1. 必要构造器就是在init前边加上关键字requird。
    2. 当子类继承了父类所有构造器后,子类就不必重写父类的必要构造器。
    3. 当子类没有继承父类的所有构造器的时候,子类必须要重写父类的必要构造器,继续在init方法前边加上requird关键字,不必加override关键字。
class Product {
    var name: String
    required init(name: String) {
        self.name = name
    }
}

class CartItem: Product {
    var sex: Int = 1
    //当该类继承了父类所有构造器的时候,就不必重写父类的必要构造器
    //当该类没有继承父类的所有构造器的时候,就要重写父类的必要构造器
    init(sex: Int, name: String) {
        self.sex = sex
        super.init(name: name)
    }
    
    required init(name: String) {
        self.sex = 0
        super.init(name: name)
    }
}

var a = CartItem(name: "s")
print(a.name)
  1. 利用闭包完成属性的初始化
class Squire {
    //通过闭包立刻给属性初始化
    let colorSquire: [Bool] = {
        var tempArr = [Bool]()
        var isBlack = false
        for _ in 1...8{
            for _ in 1...8{
                tempArr.append(isBlack)
                isBlack = !isBlack
            }
            isBlack = !isBlack
        }
        
        return tempArr
    }()
    
    //计算属性
    var squireCount: Int {
        return colorSquire.count
    }
    
    //下标
    subscript(row: Int, col: Int) -> Bool{
        return colorSquire[row * 8 + col]
    }
    
    
    func squireColor(row: Int, col: Int) -> Bool {
        return colorSquire[row * 8 + col]
    }
}

var a = Squire()
print(a.squireColor(row: 2, col: 2))
print(a[2,2])
  1. 计算属性,下标,闭包初始化属性比较

    1. 计算属性
      1. 写法是var name:Type{}
      2. 如果不需要setter方法就可以省略关键字get。get的实现是必须的。
      3. 计算属性是通过其他的存储属性计算得到的属性。
    2. 下标
      1. 写法是subscript(参数) -> type{}
      2. 如果不需要setter方法就可以省略关键字get。get的实现是必须的。
    3. 闭包初始化属性
      1. 写法是var name:type = {}()
      2. 如果不需要setter方法就可以省略关键字get。get的实现是必须的。
    4. 比较
      1. 计算属性目的是其他存储属性计算得到。{}不是等于号,不用立刻执行。
      2. 下标的目的是通过下标来访问成员。写法是必须要有关键字写法是subscript,subscript(参数) -> type{}。
      3. 闭包初始化属性,目的是立刻通过一些逻辑来完成属性的初始化,{}要有等于号,是创建实例对象时候立刻执行的()。
  2. 计算属性,下标,闭包初始化属性使用场景

    1. 该属性要通过一些存储属性的计算得到,这时候要用计算属性。
    2. 该对象要通过下标来访问内部数据,要用下标。
    3. 如果创建对象时候就要进行一些逻辑计算等来创建一个属性,就用闭包来初始化这个属性。
  3. 总结

    1. 结构体中只有指定构造器,没有遍历构造器。使用时候就是在init方法内部初始化本类属性即可。
    2. 类中既有指定构造器还有遍历构造器
      1. 指定构造器是先初始化本类属性,之后调用父类构造器。最后修改父类属性。
      2. 遍历构造器内部一定要先调用本类的指定构造器,完成所有属性初始化后再去用这些属性。
    3. 类中构造器的继承
      1. 子类中如果一个指定构造器都没有写,那么子类会继承父类的全部构造器(包括父类的指定构造器和遍历构造器)
      2. 子类如果重写了父类的所有指定构造器(可以把父类的指定构造器重写成遍历构造器),那么子类会自动继承父类的所有构造器(包括父类的指定构造器和遍历构造器)
      3. 当子类自动继承了父类的所有构造器后,子类可以选择性的重写父类的遍历构造器,并且不用使用override关键字。
    4. 结构体中默认有无参数的构造器和成员逐一构造器,并且swift5.1支持调用时候可以缺省有默认值的参数。
      类中默认只有无参数的构造器。并且一旦写了自定义的构造器,那么结构体和类中的默认构造器将会失效。