类的初始化
类的初始化是调用init()方法进行的,在 Objective-C 中,init()方法是不安全的,因为init()方法可能被调用不止一次,也不能保证初始化后所有的成员属性都不为空、都有值。
- 对于不需要立刻初始化的属性,需要在声明类型后增加 ? 符号
Designated(指定的、标记的)
所以 Swift 有了超级严格的初始化方法。一方面,Swift 强化了 designated 初始化方法的地位。Swift 中不加修饰的 init 方法都需要在方法中保证所有非 Optional 的实例变量被赋值初始化,而在子类中也强制 (显式或者隐式地) 调用 super 版本的 designated 初始化,所以无论如何走何种路径,被初始化的对象总是可以完成完整的初始化的。
class Person{
let name : String
let height : Double
let sex : String
init(personName: String, personHeight: Double, personSex: String) {
name = personName
height = personHeight
sex = personSex
}
}
class Student: Person {
let index : Int
let profession : String
init(person:Person,studentIndex:Int,studentProfession:String) {
index = studentIndex
profession = studentProfession
super.init(personName: person.name, personHeight: person.height, personSex: person.sex);
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let xiaoMingPerson = Person.init(personName: "xiaoMing", personHeight: 172.5, personSex: "男")
let xiaoMingStudent = Student.init(person: xiaoMingPerson, studentIndex: 202102, studentProfession: "汉语言文学")
print(xiaoMingStudent.name)
}
}
运行结果:
xiaoMing
在 Swift 中 let 声明的值是不变量,通常情况下无法赋值。
但我们可以发现在上述代码中,在 init() 方法里可以对 let 常量进行赋值,这是 init() 初始化方法的重要特点。
而因为 Swift 的 init() 只可能被调用一次,因此在 init 中我们可以为 let常量 进行赋值,而不会引起任何线程安全的问题。
Convenience(方便、便利)
与 designated 初始化方法对应的是在 init() 前加上 convenience 关键字的初始化方法。
我们在一些特殊的场合会有一些标准化的初始化流程,所以 convenience 的好处在于作为补充和提供使用上的方便,我们可以编写除唯一指定的初始化外其他的初始化方式。
一般是在使用系统的类进行扩充,比如UIButton,想要扩充一个UIButton(imageName:String,selected:String)构造方法,可以采用Convenience关键字进行修饰。
便利构造函数中需要调用self.init()方法。
但是所有的 convenience 初始化方法都必须调用同一个类中的 designated 初始化完成设置,另外 convenience 的初始化方法是不能被子类重写的,也不能从子类中以 super 的方式被调用。
class Person{
let name : String
let height : Double
let sex : String
init(personName: String, personHeight: Double, personSex: String) {
name = personName
height = personHeight
sex = personSex
}
//此处表示一些特殊的场合
//比如我们只需要用户名,其他属性非必须
//但始终都需要调用designated指定的初始化来实现该类有且仅被初始化一次。
convenience init(personName: String) {
self.init(personName: personName,personHeight: 0.0,personSex: "")
}
}
class Student: Person {
let index : Int
let profession : String
init(person:Person,studentIndex:Int,studentProfession:String) {
index = studentIndex
profession = studentProfession
super.init(personName: person.name, personHeight: person.height, personSex: person.sex);
}
convenience init(person:Person,studentIndex:Int) {
self.init(person: person,studentIndex: studentIndex,studentProfession: "")
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let xiaoMingPerson = Person.init(personName: "xiaoMing")
let xiaoMingStudent = Student.init(person: xiaoMingPerson, studentIndex: 202102)
print(xiaoMingStudent.name)
}
}
运行结果:
xiaoMing
只要在子类中实现重写了父类 convenience 方法所需要的init方法,我们在子类中就也可以使用父类的 convenience 初始化方法了。
- 初始化路径必须保证对象完全初始化,这可以通过调用本类型的 designated 初始化方法来得到保证。
- 子类的designated 初始化方法必须调用父类的designated 方法,以保证父类也完成初始化。
Required(子类必须重新实现)
添加 required 关键字进行限制,可以强制子类对init()方法重写实现。这样的一个最大的好处是可以保证依赖于某个 designated 初始化方法的 convenience 一直可以被使用。
class Person{
let name : String
let height : Double
let sex : String
required init(personName: String, personHeight: Double, personSex: String) {
name = personName
height = personHeight
sex = personSex
}
convenience init(personName: String) {
self.init(personName: personName,personHeight: 0.0,personSex: "")
}
}
class Student: Person {
var isStudent : Bool
required init(personName: String, personHeight: Double, personSex: String) {
isStudent = true
super.init(personName: personName, personHeight: personHeight, personSex: personSex)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let xiaoMingStudent = Student.init(personName: "xiaoMing")
print(xiaoMingStudent.isStudent)
}
}
运行结果:
true