初始化是什�?button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false">
初始化简而言之是一个准备的过程,就好比你想吃地三鲜,这时候你光在脑海里想,你是吃不到的,你需要买菜、洗菜、切菜、炒菜,然后你才能吃上地三鲜。初始化就相当于买菜、洗菜、切菜、炒菜的过程。回到代码上面,它主要做了下面两件事�?/p>
给每一个存储属性赋初始�?/li>
执行其他必须的设�?/li>
代码示例:
class PotatoPeperEggplant {
///存储属�?/span>
let potato:String
let pepper:String
let eggplant:String
init(potato:String,pepper:String,eggplant:String) {
///给每一个存储属性赋初始�?/span>
self.potato = potato
self.pepper = pepper
self.eggplant = eggplant
///执行其它必须的设�?/span>
self.cook()
}
func cook(){
//do something
}
}
let ppe = PotatoPeperEggplant(potato: "�", pepper: "�", eggplant: "�")
print("now you can eat(ppe)")
除了上面的方式,我们还可以通过设置默认值的方式来给存储属性赋值�?/p>
class PotatoPepperEggplant {
let potato = "�"
let pepper = "�"
let eggplant = "�"
init() {
cook()
}
func cook() {}
}
let ppe = PotatoPepperEggplant()
现在,我们知道了初始化就是执行构造器的过程,下面我们来看一下默认构造器和创建自定义构造器的几种方式�?/p>
默认构造器
对于值类型和引用类型,默认构造器是不同的。如�?class 给所有的存储属性赋了默认值,且没有实现任何自定义的构造器,那�?Swift 会提供一个默认的构造器�?/p>
class
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
而对�?struct ,只要没有实现任何自定义构造器,不管它有没有给存储属性赋默认值, Swift 都会提供默认构造器�?/p>
struct
struct Size {
//var width, height: Double 也会提供默认构造器
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
当你给存储属性分配默认值或者通过构造器设置初始值的时候,属性的值被直接设置,不会触发属性观�?/p>
创建自定义构造器的几种方�?button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false">
形参(parameter)构造器
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 �?100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius �?0.0
形参(parameter)和实�?argument)构造器
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)
如果声明了实参名称,调用的时候不能省略实参名称�?/p>
let veryGreen = Color(0.0, 1.0, 0.0)
// this reports a compile-time error - argument labels are required
如果你在定义构造器时没有提供实参标签,Swift 会为构造器的每个形参自动生成一个实参标签�?/p>
不带实参的形参构造器
如果你不希望构造器的某个形参使用实参标签,可以使用下划线()来代替显式的实参标签来重写默认行为�?/p>
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
有可选类型属性的构造器
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."
有常量属性的构造器
常量属性,只能在构造器中被赋值,且一旦赋值就不可修改�?code>子类中也不能修改�?br>
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()
// 打印“How about beets?�?/span>
beetsQuestion.response = "I also like beets. (But not with cheese.)"
到这里,我们了解了默认构造器和创建自定义构造器的几种方式,接下来我们看一下如果使用构造器代理( Initializer Delegation )来避免多个构造器的代码重复�?/p>
因为值类型是不能继承的,所以构造器代理又分为值类型的构造器代理和类的构造器代理,我们先看一下比较简单的值类型的构造器代理�?/p>
值类型的构造器代理
对于值类型,你可以使�?self.init 在自定义的构造器中引用相同类型中的其它构造器。并且你只能在构造器内部调用 self.init。还有就是:如果你为某个值类型定义了一个自定义的构造器,你将无法访问到默认构造器。这主要是为了避免在一个构造器中做了一些重要设置,但有人不小心使用自动生成的构造器而导致错误的情况�?/p>
如果你想让默认构造器、自定义的构造器都可以使用的话,你可以将自定义的构造器放在Extension中�?/p>
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)
}
}
init()
init(origin: Point, size: Size)
init(center: Point, size: Size)
看完上面的代�//����Ч���ο���http://www.lyjsj.net.cn/wx/art_23142.html
�你也许会有疑问�?code>init(origin: Point, size: Size)和默认构造器是一样的,那为什么我们还要再写一遍?那是因为我们自定义了init(center: Point, size: Size)构造器,所以默认构造器已经失效,我们只能再自己写一遍�?/p>如果你不想自己写一遍默认构造器的话,可以用下面这种方式实现上面等效的代码:
extension Rect {
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 提供了两种构造器,分别是:指定构造器、便利构造器�?/p>
指定构造器( Designated Initializers )
定义一个指定构造器:
init(parameters) {
statements
}
便利构造器(//����Ч���ο���http://www.lyjsj.net.cn/wz/art_23140.html
Convenience Initializers )定义一个便利构造器:
convenience init(parameters) {
statements
}
为了简化指定构造器和便利构造器之间的调用关系,Swift 构造器之间的代理调用需要遵循类类型的构造器代理规则�?/p>
类类型的构造器代理规则
规则有三条,分别是:
指定构造器必须调用其直接父类的的指定构造器�?/li>
便利构造器必须调用同类中定义的其它构造器�?/li>
便利构造器最后必须调用指定构造器�?/li>
总结一下就是:指定构造器必须总是向上代理(去父�?;便利构造器必须总是横向代理(在本�?。如下图所示:
构造器的两个阶�?button class="cnblogs-toc-button" //����Ч���ο���http://www.lyjsj.net.cn/wz/art_23138.html
title="显示目录导航" aria-expanded="false">Swift 中类的构造过程包含两个阶段。第一个阶段:给类中的每个存储属性赋初始值。只要每个存储属性初始值被赋值,第二阶段开始,它给每个类一次机会,在新实例准备使用之前进一步自定义它们的存储属性�?br>Swift 通过4步安全检查来确定构造器两个阶段的成功执行:
安全检�?:指定构造器必须在完成本类所有存储属性赋值之后,才能向上代理到父类的构造器�?br>
class Animal {
var head = 1
}
class Dog: Animal {
var foot: Int
override init() {
super.init()
foot = 4
}
}
上面�?code>super.init()会报错,因为此时 Dog �?foot 还没有被赋值。将 init() 改为下面即可�?/p>
override init() {
foot = 4
//这句也可以省略,它默认是隐式调用的�?/span>
super.init()
}
安全检�?:指定构造器必须在为继承的属性设置新值之前向上代理调用父类构造器�?br>
//这时,你必须显式的调用super.init(),因为你要修改继承属�? head 的�?/span>
override init() {
foot = 4
super.init()
head = 2
}
安全检�?:便利构造器必须先调用其他构造器,再为任意属性(包括所有同类中定义的)赋新值�?br>
convenience init(foot: Int) {
//先调用其他构造器,如果此处不调用会编译出�?/span>
self.init()
//再为任意属性(包括所有同类中定义的)赋新�?/span>
self.foot = foot
head = 3
}
安全检�?:构造器在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引用 self 作为一个值�?br>
class Dog: Animal {
var foot: Int
override init() {
foot = 4
super.init()
head = 2
// 如果上面的未完成,是不能调用run()的,因为self还没有完整的创建
run()
}
func run() {
//do something
}
}
现在看一下阶段一和阶段二的完整流程:
阶段 1 - 自下而上
类的某个指定构造器或便利构造器被调用�?
完成类的新实例内存的分配,但此时内存还没有被初始化�?
指定构造器确保其所在类引入的所有存储型属性都已赋初值�?
存储型属性所属的内存完成初始化�?
指定构造器切换到父类的构造器,对其存储属性完成相同的任务�?
这个过程沿着类的继承链一直往上执行,直到到达继承链的最顶部�?
当到达了继承链最顶部,而且继承链的最后一个类已确保所有的存储型属性都已经赋值,
这个实例的内存被认为已经完全初始化。此时阶�?1 完成�?
阶段 2 - 自上而下
从继承链顶部往下,继承链中每个类的指定构造器都有机会进一步自定义实例�?
构造器此时可以访问 self、修改它的属性并调用实例方法等等�?
最终,继承链中任意的便利构造器有机会自定义实例和使�?self�?/pre>
第一阶段示例�?- 自下而上:
第二阶段示例�?- 自上而下�?/div>
构造器的继承与重写
继承 默认情况下子类是不会继承父类的构造器。但是如果满足特定条件,父类构造器是可以被子类自动继承�?br>
规则 1
如果子类没有定义任何指定构造器,它将自动继承父类所有的指定构造器�?
规则 2
如果子类提供了所有父类指定构造器的实现——无论是通过规则 1
继承过来的,还是提供了自定义实现——它将自动继承父类所有的便利构造器�?
class Animal {
let head = 1
var name = ""
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "animal")
}
}
class Dog: Animal {
let foot = 4
}
//自动继承父类所有的指定构�?/span>
let d1 = Dog(name: "dog") // d1.name dog
//自动继承父类所有的便利构造器
let d2 = Dog() // d2.name animal
重写
class Vehicle {
var numberOfWheels = 0
var description: String {
return "(numberOfWheels) wheel(s)"
}
}
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
<div class="cnblogs_code_t