初始化的过程包括为每个存储属性设置一个初始值和其余步骤。经过定义构造函数来实现初始化的过程,跟oc的初始化函数不一样,Swift的构造函数不返回一个值。它们的主要角色是确保一个类型的实例在初次使用前被正确的初始化。swift
类的实例也能够有析构函数,析构函数在类的实例在释放前完成一些清理工做。安全
类和结构体必须为它们全部的存储属性设置一个初始值,在类或结构体的实例建立完成前。存储属性不能是不肯定的状态。ide
能够经过构造函数或默认值的方式给存储属性一个初始值,并且经过这两种方式,属性的值都是被直接设置,不会调用属性观察者。函数
init() { }
你能够自定义初始化过程,经过使用输入参数,可选的属性类型,或者在初始化期间给常量属性赋值。具体描述在下面的部分中描述。3d
你能够为构造函数提供参数,看个例子:code
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 boilingPointOfWater = Celsius(fromFahrenheit: 212.0) let freezingPointOfWater = Celsius(fromKelvin: 273.5) let bodyTemperature = Celsius(37.0)
Swift provides an autoamtic argument label for every parameter in an initializer if you don't provide one.看个例子:orm
struct Color { var 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 } }
咱们能够这样初始化一个Color
的实例:blog
let megenta = Color(red: 1.0, green: 0.0, blue: 1.0) let halfGray = Color(white: 0.5)
参考上边Celsius
类中的第三个构造方法,及bodyTemperature
的初始化。继承
若是你自定义的类型有一个存储属性在逻辑上容许“没有值”——或许是由于在初始化期间不能设置设置它的值,也多是在后边的某个时间点容许它“没有值”,那么能够把这个属性声明为optional type。optional type的属性被自动初始化为nil
。原文以下:ip
“If your custom type has a stored property that is logically allowed to have “no value”—perhaps because its value cannot be set during initialization, or because it is allowed to have “no value” at some later point—declare the property with an optional type. Properties of optional type are automatically initialized with a value of nil, indicating that the property is deliberately intended to have “no value yet” during initialization.”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 3).” iBooks.
看下下边这个例子:
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() cheeseQuestion.response = "YES, i do like chesse."
你能够在初始化期间给一个常量属性赋一个值,只要在初始化完成时有一个明确的值就能够。
注意:对于类的实例,常量属性只能由引入该属性的类在初始化期间修改,不能被子类修改。
能够在上边那个类稍做修改,观察text属性:
class SurveyQuestion { let 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() cheeseQuestion.response = "YES, i do like chesse."
在类或结构体为每一个属性提供默认值而且没有其余构造函数时,Swift提供默认构造函数。默认构造函数简单的建立一个实例并把其全部属性设置成默认值。
class ShoppingList { var name: String? var quantity = 1 var purchased = false } var item = ShoppingList()
上面的例子中,若是咱们不给quantity
设置默认值,编译器将提示ShoppingList
类没有构造函数。
结构体类型若是没有自定义的构造函数,那么会有一个memberwise initializer。
struct Size { var width = 0.0, height = 0.0 } let twoByTwo = Size(width: 2.0, height: 2.0)
Initializers can call other initializers to perform part of an instance's initialization. This process, known as initializer delegation, avoids duplicating code across multiple initializers.
对于值类型,可使用self.init
来引用该类型的其余构造函数。self.init
只能在构造函数内使用。看个例子:
struct Size { var width = 0.0, height = 0.0 } struct Point { var x = 0.0, y = 0.0 } struct Rect { var size = Size() var point = Point() init() {} init(size: Size, point: Point) { self.size = size self.point = point } init(size: Size, center: Point) { let originX = center.x - size.width / 2 let originY = center.y - size.height / 2 self.init(size: size, point: Point(x: originX, y: originY)) } }
上面例子中第一个构造函数其实就至关于默认构造函数,第二个至关于memberwise initializer,第三个是彻底自定义的。这个例子还有一种写法,能够不写init()
和init(size:point:)
,须要用到extension。
若是为值类型自定义了一个构造函数,那就不能再访问默认构造函数了。若是你想要你定义的值类型既可使用默认构造函数,也可使用自定义的构造函数,那须要把自定义的构造函数写在extension中。
一个类全部的存储属性包括从父类继承的属性,都以徐在初始化几千有一个初始值。
Swift为类类型定义了两种类型的构造函数来确保存储属性有一个初始值,分别是designated initializer和convenience initializer。在下面的讨论中,咱们分别把这两种称为指定构造函数、便利构造函数。
每一个类都必须至少有一个指定构造函数。在一些条件下,这个需求经过从父类继承指定构造函数来知足。
便利构造函数能够调用该类中的指定构造函数。
指定构造函数的语法:
便利构造函数的语法:
为了简化指定构造函数与遍历构造函数之间的关系,Swift在构造函数间的delegation call上使用下边三条规则:
能够简单的这样记:
这里有一个示意图来讲明上面描述的规则:
在Swift中类的初始化分为两个阶段。在第一个阶段,类的每个存储属性由引入其的类赋一个初始值,一旦每个存储属性的初始状态肯定了,第二个几段就开始了。第二个阶段开始之后,每一个类在类可使用前都有机会给每个存储属性进行进一步自定义。
跟oc中的初始化有些许不一样,主要体如今第一个阶段,在第一个阶段中,oc给每个属性赋0值(如0、nil)。
Swift的编译器执行四步安全性检查(safaty-check)来确保两个阶段的初始化没有问题:
self
as a value until after the first phase of initialization is complete.跟oc不一样,Swift中的子类默认不继承父类的构造方法。当你为子类定义了一个构造方法,这个构造方法match一个父类的指定构造方法,你其实是对那个指定构造方法进行了重写。所以,本身在子类构造方法前边用override
标记。这个原则对于默认构造方法也适用。看个例子:
class Vehicle { var numberOfWheels = 0 var description: String { return "\(numberOfWheels) wheel(s)" } } class Bicycle : Vehicle { override init() { super.init() numberOfWheels = 2 } }
正如上边提到的,默认状况下,子类不继承弗雷德构造方法。可是,在知足一些条件的前提下,父类的构造方法能够被自动继承。
假设你已经为子类新引入的属性提供了默认值,那么有下面两条规则适用:
这些规则即便在子类添加了便利构造方法时,依然适用。
在这一部分,咱们经过一个例子来解释上边的两条规则。
class Food { var name: String //指定构造方法 init(name: String) { print("super versioin") self.name = name } //便利构造方法 convenience init() { self.init(name: "[Unnamed]") } } class RecipeIngredient: Food { var quantity: Int init(name: String, quantity: Int) { self.quantity = quantity super.init(name: name) } override convenience init(name: String) { print("sub version") self.init(name: name, quantity: 1) } }
如今这两个类的构造方法能够用下标的图来清楚的展现:
咱们直接看一下RecipeIngredient
类,首先有一个自定义的指定构造方法init(name:quantity:)
,而后有一个自定义的便利构造方法init(name:)
,这个方法其实是重写了Food
类的惟一的一个指定构造方法,因此知足了上面的第二条原则,所以自动继承Food
类全部的便利构造方法,即init()
方法。这里须要注意的是,RecipeIngredient
继承来的init()
方法中,调用的init(name:)
不是Food
版本的,而是RecipeIngredient
版本的。
如今再来定义一个类:
class ShoppingListItem: RecipeIngredient { var purchased = false var description: String { var output = "\(quantity) * \(name)" output += purchased ? "✔️" : "❌" return output } }
由于ShoppingListItem
自己没有定义任何的构造方法,所以它从父类继承全部的构造方法。此时,示意图以下: