Initialization(初始化)
初始化是准备一个类,结构体或枚举的实例的过程。
为存储属性设置初始值
当类或结构体的实例被创建时,你必须为它们的属性初始化一个合适的值。
这些属性可以在初始化器中赋值,也可以在定义属性时赋一个默认值。
Initializers(初始化器)
使用init关键字定义初始化器
init() {
// perform some initialization here
}
使用初始化器初始化一个结构体
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// Prints "The default temperature is 32.0° Fahrenheit"
属性的默认值
如果每次初始化时,属性的值都一样,最好给这个属性设置默认值:
struct Fahrenheit {
var temperature = 32.0
}
初始化参数
定义带参数的初始化器
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0
参数名和参数标签
同函数和方法一样,初始化器参数也可以提供在初始化器内部使用的参数名,和外部调用时使用的参数标签名。
标签名:没有特别指定时,参数名和标签名相同
struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}
调用初始化时必须使用标签名:
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)
不然,编译时就会报错:
let veryGreen = Color(0.0, 1.0, 0.0)
// this reports a compile-time error - argument labels are required
没有标签名的初始化器参数
与函数和方法类似,在初始化器的参数中,可以用“_”隐藏外部调用时需要的标签名
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
}
let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius is 37.0
可选类型属性(Optional)
可选类型的属性,可以不用在初始化时设置值,因为可选类型的属性允许分配nil值
class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
cheeseQuestion.ask()
// Prints "Do you like cheese?"
cheeseQuestion.response = "Yes, I do like cheese."
初始化时给常量属性赋值
常量只能赋默认值或在初始化器中赋值,这与Java类似
class SurveyQuestion {
let text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// Prints "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)"
默认初始化器
Swift自动的为类和结构体提供了一个默认的初始化器
默认为类提供了一个无参的初始化器
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
默认为结构体提供了一个按顺序包含所有参数的初始化器
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
值类型的初始化器委托
在实例的初始化过程中,一个初始化器可以调用其他初始化器,这个过程叫初始化器委托。这可以避免多个初始化器中出现重复的代码。
值类型和引用类型的委托规则是不一样的。值类型不支持继承(结构体,枚举),所以初始化器委托只能调用自身其他的初始化器。而类支持继承,因此类还有确保所有继承的属性被赋值了一个合理的值。
结构体初始化器委托
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
init() {}
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
类的继承和初始化
类的所有属性(包括从父类继承的属性)在初始化期间都有被赋值一个合适的值。
为此Swift提供了designated初始化器和convenience初始化器,方便类初始化
designated初始化器和convenience初始化器
designated初始化器是类的初始初始器,它必须初始化本类定义的所有需要初始化的属性,调用父类合适的初始化器,完成类的向上初始化执行链条。
每一个类必须至少有一个designated初始化器。
convenience初始化器,是一个间接的初始化器。你可以定义convenience初始化器调用designated 初始化器,也可以直接使用convenience初始化器实例化一个类。
convenience初始化器在类中需要的时候才定义。
designated初始化器和convenience初始化器语法
designated初始化器语法
init(parameters) {
statements
}
convenience初始化器语法
convenience init(parameters) {
statements
}
引用类型的初始化器委托
为了简化 designated 和 convenience 初始化器的关系,Swift应用三种简单的规则:
- 规则1,designated 初始化器,必须调用一个直接父类的designated初始化器。
- 规则2,convenience初始化器, 必须调用同类的designated初始化器。
- 规则3,convenience初始化器,最终必须调用一个designated初始化器。
简单的来讲:
- designated 初始化器向上委托
- convenience初始化器横向委托
如图:
在父类中定义了一个designated 初始化器和两个convenience初始化器,一个convenience初始化器调用了另一个convenience初始化器,然后这个convenience初始化器调用了designated 初始化器。
在子类中,定义了两个designated 初始化器和一个convenience初始化器,一个convenience初始化器调用了一个designated 初始化器,然后这个designated 初始化器调用了父类的designated 初始化器。另一个designated 初始化器调用父类的designated初始化器。
下图是一个更复杂的类的层级调用图:
两阶段初始化
第一阶段,给每一个属性赋初始值。
第二阶段,给每一个实例在被认为可以使用之前,定制属性的机会。
Swift编译器执行四种有用的安全检查,确保两个阶段的初始化正确完成。
- Safety check 1 , designated 初始化器必须在调用父类的designated 初始化器之前,所有本类的属性被初始化了。
- Safety check 2,designated 初始化器必须在给父类属性赋值之前调用父类的designated 初始化器,赋值调用父类的designated 初始化器时会覆盖掉子类给父类属性赋的值。
- Safety check 3,convenience 初始化器必须在给任何属性赋值之前调用其他初始化器,否则会被本地的designated 初始化器覆盖掉赋的值。
- Safety check 4,第一阶段的初始化完成之前,初始化器,不能调用任何实例方法,不能使用self相关的表达式。
基于上面提到的四个安全检查,两阶段初始化的运行过程:
第一阶段:
- 调用designated 或 convenience初始化器
- 新实例的内存被分配,但还没有初始化
- 类的designated 初始化器确认了当前类的所有属性有值,属性的内存还没有被初始化。
- designated 初始化调用父类初始器,父类的初始化器对它自己的属性做相同的事情。
- 继续向上调用直到最顶层父类。
- 到达最顶层父类后,所有的属性已经都被确认有值了,然后实例的内存被初始化,第一阶段完成。
第二阶段
- 按照调用链,从顶层父类回到当前类,在链中的每一个designated 初始化器可以有选择的更深入的定制实例,现在在初始化器中可以访问self,可以修改自己的属性,可以访问实例方法,等。
- 最后,链中的任何convenience 初始化器有选择的定制实例,使用self。
初始化器的继承和重写
在Swift中,子类不会默认继承父类的初始化器。
如果你在子类实现的初始化器与父类的初始化器匹配,那么你已经重写了父类的初始化器,你就需要使用override修饰符。
和重写属性,方法,脚本一样,override修饰符,让Swift检查父类是否有被重写的初始化器.
注意
即使是convenience初始化器与父类的designated初始化器匹配也要写override修饰符
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
init(numberOfWheels:Int){
self.numberOfWheels = numberOfWheels
}
}
class Bicycle: Vehicle {
init(num:Int){
super.init(numberOfWheels:num)
}
override convenience init(numberOfWheels:Int) {
self.init(num:numberOfWheels)
}
}
相反地,如果子类的designated初始化器与父类的convenience初始化器匹配,根据引用类型初始化器的委托规则,父类的convenience初始化器永远不可能被调到,因此不用加override修饰符。
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
convenience init(numberOfWheels:Int) {
self.init()
self.numberOfWheels = numberOfWheels
}
}
class Bicycle: Vehicle {
init(numberOfWheels:Int){
super.init()
self.numberOfWheels = numberOfWheels
}
}
let b = Bicycle(numberOfWheels:2)
print(b.description)
初始化器的自动继承
前面提到,子类不会默认的继承父类的初始化器,但是在一些条件下,子类会默认继承父类的初始化器。
假定,子类的所有属性都有默认值,那么子类的自动继承遵循两个规则:
- 规则1,如果子类没有定义任何designated 初始化器,子类默认继承父类所有的designated 初始化器
- 如果子类定义了一个designated初始化器,或者是继承父类的designated初始化器,子类自动继承所有父类的convenience 初始化器。
Designated and Convenience Initializers in Action
定义Food类
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
调用designated 初始化器,完成实例的初始化。
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon"
调用convenience 初始化器,完成实例的初始化。
let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]"
调用过程如图:
定义RecipeIngredient类,并继承Food
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)
}
}
按照自动继承规则它继承了父类Food的convenience初始化器init().
这三种方式都可以初始化RecipeIngredient。
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
调用过程:
定义ShoppingListItem类,并继承RecipeIngredient
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? " ✔" : " ✘"
return output
}
}
按照初始化器的自动继承规则,它继承了父类RecipeIngredient所有的初始化器:两个convenience 初始化器init(),init(name:) ,一个designated初始化器init(name:,quantity:)
如图:
可失败的初始化器
使用"?",定义一个可失败的初始化器:
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
使用可失败的初始化器初始化的实例是optional 类型的,可以为nil
let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
// Prints "An animal was initialized with a species of Giraffe"
枚举类型中定义可失败的初始化器
enum TemperatureUnit {
case kelvin, celsius, fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .kelvin
case "C":
self = .celsius
case "F":
self = .fahrenheit
default:
return nil
}
}
}
使用可失败初始化器对TemperatureUnit初始化
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
枚举使用行值定义的可失败初始化器
枚举自动生成以行值为参数的可失败初始化器。
enum TemperatureUnit: Character {
case kelvin = "K", celsius = "C", fahrenheit = "F"
}
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
没有在枚举TemperatureUnit定义初始化器TemperatureUnit(rawValue:),但是可以使用TemperatureUnit(rawValue:)对枚举的实例初始化。
初始化失败的传播
类,结构体,枚举的可失败初始化器可以调用同类型中其他的可失败初始化器。
子类也可以调用父类的可失败初始化器。
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
class CartItem: Product {
let quantity: Int
init?(name: String, quantity: Int) {
if quantity < 1 { return nil }
self.quantity = quantity
super.init(name: name)
}
}
可失败初始化器调用链中有一个失败就会理解返回nil
if let oneUnnamed = CartItem(name: "", quantity: 1) {
print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
print("Unable to initialize one unnamed product")
}
// Prints "Unable to initialize one unnamed product"
覆盖可失败的初始化器
在子类中,可以像覆盖其他初始化器那样覆盖可失败初始化器。
也可以使用不可失败初始化器覆盖父类的可失败初始化器。
父类Document定义了一个可失败的初始化器 init?(name:):
class Document {
var name: String?
// this initializer creates a document with a nil name value
init() {}
// this initializer creates a document with a nonempty name value
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
子类AutomaticallyNamedDocument用不可失败的初始化器 init(name:)覆盖了父类的可失败初始化器 init?(name:):
class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
}
override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
} else {
self.name = name
}
}
}
你可以在子类的不可失败初始化器中调用父类的可失败初始化器,如果父类的可失败初始化器返回nil,程序会报运行时错误。
class UntitledDocument: Document {
override init() {
super.init(name: "[Untitled]")!
}
}
可失败的初始化器init!
init!生成隐式可选类型的实例。
- 可以在init!初始化器中调用init?。
- 可以在init?中调用init!.
- 可以在init中调用init!,如果init!返回nil,会报运行时错误。
Required Initializers
使用required 修饰符定义的初始化器,在每一个子类中必须实现这个初始化器。
在父类中定义了required初始化器。
class SomeClass {
required init() {
// initializer implementation goes here
}
}
在子类中覆盖了父类的required初始化器。覆盖required初始化器不需要写override修饰符。
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
用闭包或者函数设置属性的默认值
使用全局函数设置默认值。
func someMethod()->String{
return "hello,my world"
}
class SomeClass{
let h=someMethod()
}
var t = SomeClass()
print(t.h)
//hello,my world
使用闭包设置默认值
class SomeClass {
let someProperty: SomeType = {
// create a default value for someProperty inside this closure
// someValue must be of the same type as SomeType
return someValue
}()
}
在闭包后面加括号,告诉Swift要执行闭包。如果没有加括号,是把闭包赋值给了属性而不是把闭包的返回值给了属性。
注意
在使用闭包时其他属性还没有被初始化,不能使用其他属性,即使属性有默认值也不能使用,不能使用self。