什么是初始化
初始化就是:给存储属性赋值。像下面这样
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
注意:当你对给一个属性分配一个默认值的时候,它会调用它相对应的初始化方法,这个值是对属性直接设置的,不会通知它对应的观察者。
下面有2种初始化方法:
- 默认初始化方法
Swift为每一个结构或者基类提供了默认的构造器,来初始化它们所包含的所有属性。默认构造器将会创建一个新的实例然后将它们的属性设置为默认值。
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
- 自定义初始化
你可以根据输入的参数来自定义初始化过程和可选的属性类型,或者在初始化的时候修改静态属性。
带参数的初始化方法叫自定义初始化。
struct Celsius {
var temperatureInCelsius: Double = 0.0
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
当然上面只是基础知识几个概念,下面才是重点。
类的继承及其初始化
- 类的初始化过程
两段式构造过程
Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性通过引入它们的类的构造器来设置初始值。当每一个存储型属性值被确定后,第二阶段开始,它给每个类一次机会在新实例准备使用之前进一步定制它们的存储型属性。
阶段 1
某个指定构造器或便利构造器被调用;
完成新实例内存的分配,但此时内存还没有被初始化;
指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化;
指定构造器将调用父类的构造器,完成父类属性的初始化;
这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部;
当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段1完成。
阶段 2
从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问self、修改它的属性并调用实例方法等等。
最终,任意构造器链中的便利构造器可以有机会定制实例和使用self。
- 如何初始化
Paste_Image.png1、自定义初始化方法要先调用自己类默认初始化方法,自己重写默认初始化方法要先调用父类默认初始化方法
2、应该要先调用父类的构造器或者自身的默认构造器,以防止先给属性赋值了然后才调用父类或者自身的默认构造器把以前的赋值覆盖了
- 关于初始化的继承
规则 1
如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
规则 2
如果子类提供了所有父类指定构造器的实现--不管是通过规则1继承过来的,还是通过自定义实现的--它将自动继承所有父类的便利构造器。
- Swift的4种安全检查
安全检查 1
指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。
安全检查 2
指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。
安全检查 3
便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。
安全检查 4
构造器在第一阶段构造完成之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用self的值。
代码实例
class Food {
var name: String
//指定的初始化器
init(name: String) {
self.name = name
}
//便携初始化器
convenience init() {
self.init(name: "[Unnamed]")
}
}
Paste_Image.png
class RecipeIngredient: Food {
var quantity: Int
//重载指定初始化器
init(name: String, quantity: Int) {
//首先初始化自己本类的存储属性
self.quantity = quantity
//然后调用初始化父类的存储属性
super.init(name: name)
}
convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
Paste_Image.png
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name.lowercaseString)"
output += purchased ? " ?" : " ?"
return output
}
}
由于它为自己引入的所有属性都提供了默认值,并且自己没有定义任何构造器,ShoppingListItem将自动继承所有父类中的指定构造器和便利构造器。
网友评论
var quantity: Int
//重载指定初始化器
init(name: String, quantity: Int) {
//首先初始化自己本类的存储属性
self.quantity = quantity
//然后调用初始化父类的存储属性
super.init(name: name)
}
convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
不是说在初始化第一阶段完成前是不能引用self嘛?为什么这里在调用super.init(name: name) 之前使用self?