Swift5.1学习随笔之初始化器

2020-04-28  本文已影响0人  SAW_

初始化器

指定初始化器

init(parameters) {
    statements
}

便捷初始化器,添加关键字convenience

convenience init(parameters) {
    statements
}

1、每个类至少有一个指定初始化器,指定初始化器是类的主要初始化器

不指定初始化器,编译器自动生成init() {}初始化器

class Size {
    var width: Int = 0
    var height: Int = 0
    init() {} // 编译器自动生成,可以不写
}
var s = Size() 

自定义了初始化器,系统就不会生成默认的init() {}初始化器

class Size {
    var width: Int = 0
    var height: Int = 0
    init(width: Int, height: Int) {
        self.width = width
        self.height = height
    }
}
var s = Size() // 报错:Missing arguments for parameters 'width', 'height' in call
var s = Size(width: 10, height: 10) // 正确调用

2、默认初始化器总是类的指定初始化器
3、类偏向于少量指定初始化器,一个类通常只有一个指定初始化器
4、初始化器的相互调用规则

便捷初始化器必须调用一个指定初始化器
以下代码调用OK

class Size {
    var width: Int = 0
    var height: Int = 0
    init() {
        
    }
    init(width: Int, height: Int) {
        self.width = width
        self.height = height
    }
    init(width: Int) {
        self.width = width
    }
    init(height: Int) {
        self.height = height
    }
}
var s1 = Size()
var s2 = Size(width: 10, height: 10)
var s3 = Size(width: 10)
var s4 = Size(height: 10)

init(width: Int, height: Int)加上convenience关键词,变成便捷初始化器,编译器报错,提示内部必须调用一个指定初始化器

convenience init(width: Int, height: Int) {
    self.width = width
    self.height = height
} // 报错:'self.init' isn't called on all paths before returning from initializer

修改之后,内部可以调用init()init(width: Int)init(height: Int)任何一个

convenience init(width: Int, height: Int) {
    self.init() 
    self.width = width
    self.height = height
}
convenience init(width: Int, height: Int) {
    self.init(width: width)
    self.height = height
}
convenience init(width: Int, height: Int) {
    self.init(height: height)
    self.width = width
}

Swift设计的时候出于安全考虑,必须满足任何一个初始化器调用的时候,保证所有属性都是有值。

指定初始化器必须从它的直系父类调用指定初始化器

class Person {
    var age: Int
    init(age: Int) {
        self.age = age
    }
    convenience init() {
        self.init(age: 0)
    }
}
class Student: Person {
    var score: Int
    init(age: Int, score: Int) {
        self.age = age // 报错:'self' used in property access 'age' before 'super.init' call
        self.score = score
    } //报错:'super.init' isn't called on all paths before returning from initializer
}

修改:必须调用父类指定初始化器,必须放置后面实现

class Student: Person {
    var score: Int
    init(age: Int, score: Int) {
        self.score = score
        super.init(age: age) //必须调用父类指定初始化器,必须放置后面实现
    }
}
两段式初始化

Swift在编码安全方面做了很多,为了保证初始化过程的安全,设置了两段式初始化、安全检查

第1阶段:初始化所有存储属性

第2阶段:设置新的存储属性值

自动继承
  1. 如果子类没有自定义任何指定初始化器,它会自动继承父类所有的指定初始化器
class Person {
    var age: Int
    var name: String
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
    init(age: Int) {
        self.age = age
        self.name = ""
    }
}

class Student: Person {
    
}

var s1 = Student() //报错:Missing argument for parameter 'age' in call
var s2 = Student(age: 10) //继承父类的初始化器
var s3 = Student(age: 10, name: "jack") //继承父类的初始化器
  1. 如果子类提供了父类所有指定初始化器的实现(要么通过1继承,要么重写)
    • 子类自动继承所有的父类便捷初始化器
class Person {
    var age: Int
    var name: String
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
    init() {
        self.age = 0
        self.name = ""
    }
    convenience init(age: Int) {
        self.init(age: age, name: "")
    }
    convenience init(name: String) {
        self.init(age: 0, name: name)
    }
}

class Student: Person {
    //子类没有实现任何初始化器
}

从下面截图中可以看出,子类Student拥有了父类Person定义的所有初始化器

  1. 就算子类添加了更多的便捷初始化器,这些规则依然适用
  2. 子类以便捷初始化器的形式重写父类的指定初始化器,也可以作为满足规则2的一部分
可失败初始化器

1.类、结构体、枚举都可以用init?定义可失败初始化器

class Person {
    var name: String
    init?(name: String) {
        if name.isEmpty { 失败
            return nil //如果传入的字符串为空,就返回nil,初始化
        }
        self.name = name
    }
}
var p1 = Person(name: "")
var p2 = Person(name: "jack")
print(p1) // nil
print(p2) // Optional(SwiftDDDDD.Person)
  1. 不允许同时定义参数标签、参数个数、参数类型相同的可失败初始化器和非可失败初始化器
class Person {
    var name: String
    init?(name: String) {
        if name.isEmpty {
            return nil
        }
        self.name = name
    }
    init(name: String) { //报错:Invalid redeclaration of 'init(name:)'
        self.name = name
    }
}
  1. 可以使用init!定义隐式解包的可失败初始化器
  2. 可失败初始化器可以调用非可失败初始化器,非可失败初始化器调用可失败初始化器需要进行解包
class Person {
    var name: String
    convenience init?(name: String) {
        self.init()
        if name.isEmpty {
            return nil
        }
    }
    init() {
        self.name = ""
    }
}
class Person {
    var name: String
    init?(name: String) {
        if name.isEmpty {
            return nil
        }
        self.name = name
    }
    convenience init() {
        self.init(name: "")!
    }
}
class Person {
    var name: String
    init!(name: String) {
        if name.isEmpty {
            return nil
        }
        self.name = name
    }
    convenience init() {
        self.init(name: "")
    }
}
上一篇 下一篇

猜你喜欢

热点阅读