PDF版 ePub版

# 构造过程（Initialization）

## 存储型属性的初始赋值

### 构造器

    struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
    var f = Fahrenheit()
println("The default temperature is \(f.temperature)° Fahrenheit")
// 输出 "The default temperature is 32.0° Fahrenheit”

### 默认属性值

    struct Fahrenheit {
var temperature = 32.0
}

## 定制化构造过程

### 构造参数

    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
}
}
    let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius 是 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius 是 0.0”

### 内部和外部参数名

Color提供了一个构造器，其中包含三个Double类型的构造参数：

    struct Color {
let red = 0.0, green = 0.0, blue = 0.0
init(red: Double, green: Double, blue: Double) {
self.red   = red
self.green = green
self.blue  = blue
}
}

    let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)

    let veryGreen = Color(0.0, 1.0, 0.0)
// 报编译时错误，需要外部名称

### 可选属性类型

    class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
self.text = text
}
println(text)
}
}
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
// 输出 "Do you like cheese?"
cheeseQuestion.response = "Yes, I do like cheese."

### 构造过程中常量属性的修改

    class SurveyQuestion {
let text: String
var response: String?
init(text: String) {
self.text = text
}
println(text)
}
}
let beetsQuestion = SurveyQuestion(text: "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)
}
}

    let basicRect = Rect()
// basicRect 的原点是 (0.0, 0.0)，尺寸是 (0.0, 0.0)

    let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
// originRect 的原点是 (2.0, 2.0)，尺寸是 (5.0, 5.0)

    let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
// centerRect 的原点是 (2.5, 2.5)，尺寸是 (3.0, 3.0)

## 类的继承和构造过程

Swift 提供了两种类型的类构造器来确保所有类实例中存储型属性都能获得初始值，它们分别是指定构造器和便利构造器。

### 构造器链

#### 规则 3

• 指定构造器必须总是向上代理
• 便利构造器必须总是横向代理

### 两段式构造过程

Swift 中类的构造过程包含两个阶段。第一个阶段，每个存储型属性通过引入它们的类的构造器来设置初始值。当每一个存储型属性值被确定后，第二阶段开始，它给每个类一次机会在新实例准备使用之前进一步定制它们的存储型属性。

Swift的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1，Objective-C 给每一个属性赋值0或空值（比如说0nil）。Swift 的构造流程则更加灵活，它允许你设置定制的初始值，并自如应对某些属性不能以0nil作为合法默认值的情况。

Swift 编译器将执行 4 种有效的安全检查，以确保两段式构造过程能顺利完成：

#### 阶段 1

• 某个指定构造器或便利构造器被调用；
• 完成新实例内存的分配，但此时内存还没有被初始化；
• 指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化；
• 指定构造器将调用父类的构造器，完成父类属性的初始化；
• 这个调用父类构造器的过程沿着构造器链一直往上执行，直到到达构造器链的最顶部；
• 当到达了构造器链最顶部，且已确保所有实例包含的存储型属性都已经赋值，这个实例的内存被认为已经完全初始化。此时阶段1完成。

#### 阶段 2

• 从顶部构造器链一直往下，每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问self、修改它的属性并调用实例方法等等。
• 最终，任意构造器链中的便利构造器可以有机会定制实例和使用self

### 指定构造器和便利构造器的语法

    init(parameters) {
statements
}

    convenience init(parameters) {
statements
}

### 指定构造器和便利构造器实战

    class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}

    let namedMeat = Food(name: "Bacon")
// namedMeat 的名字是 "Bacon”

Food类中的构造器init(name: String)被定义为一个指定构造器，因为它能确保所有新Food实例的中存储型属性都被初始化。Food类没有父类，所以init(name: String)构造器不需要调用super.init()来完成构造。

Food类同样提供了一个没有参数的便利构造器 init()。这个init()构造器为新食物提供了一个默认的占位名字，通过代理调用同一类中定义的指定构造器init(name: String)并给参数name传值[Unnamed]来实现：

    let mysteryMeat = Food()
// mysteryMeat 的名字是 [Unnamed]

    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)
}
}

RecipeIngredient类拥有一个指定构造器init(name: String, quantity: Int)，它可以用来产生新RecipeIngredient实例的所有属性值。这个构造器一开始先将传入的quantity参数赋值给quantity属性，这个属性也是唯一在RecipeIngredient中新引入的属性。随后，构造器将任务向上代理给父类Foodinit(name: String)。这个过程满足两段式构造过程中的安全检查1。

RecipeIngredient也定义了一个便利构造器init(name: String)，它只通过name来创建RecipeIngredient的实例。这个便利构造器假设任意RecipeIngredient实例的quantity为1，所以不需要显示指明数量即可创建出实例。这个便利构造器的定义可以让创建实例更加方便和快捷，并且避免了使用重复的代码来创建多个quantity为 1 的RecipeIngredient实例。这个便利构造器只是简单的将任务代理给了同一类里提供的指定构造器。

    let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)

    class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name.lowercaseString)"
output += purchased ? " ✔" : " ✘"
return output
}
}

ShoppingListItem没有定义构造器来为purchased提供初始化值，这是因为任何添加到购物单的项的初始状态总是未购买。

    var breakfastList = [
ShoppingListItem(),
ShoppingListItem(name: "Bacon"),
ShoppingListItem(name: "Eggs", quantity: 6),
]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
println(item.description)
}
// 1 x orange juice ✔
// 1 x bacon ✘
// 6 x eggs ✘

## 可失败构造器

    struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}

    let someCreature = Animal(species: "Giraffe")
// someCreature 的类型是 Animal? 而不是 Animal
if let giraffe = someCreature {
println("An animal was initialized with a species of \(giraffe.species)")
}
// 打印 "An animal was initialized with a species of Giraffe"

    let anonymousCreature = Animal(species: "")
// anonymousCreature 的类型是 Animal?, 而不是 Animal
if anonymousCreature == nil {
println("The anonymous creature could not be initialized")
}
// 打印 "The anonymous creature could not be initialized"

### 枚举类型的可失败构造器

    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
}
}
}

    let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
println("This is a defined temperature unit, so initialization succeeded.")
}
// 打印 "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
println("This is not a defined temperature unit, so initialization failed.")
}
// 打印 "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 {
println("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 {
println("This is not a defined temperature unit, so initialization failed.")
}
// prints "This is not a defined temperature unit, so initialization failed."


### 类的可失败构造器

    class Product {
let name: String!
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}

    if let bowTie = Product(name: "bow tie") {
// 不需要检查 bowTie.name == nil
println("The product's name is \(bowTie.name)")
}
// 打印 "The product's name is bow tie"

### 构造失败的传递

    class CartItem: Product {
let quantity: Int!
init?(name: String, quantity: Int) {
super.init(name: name)
if quantity < 1 { return nil }
self.quantity = quantity
}
}

Product类中的name属性相类似的，CartItem类中的quantity属性的类型也是一个隐式解析可选类型，只不过由（String！）变为了（Int!）。这样做都是为了确保在构造过程中，该属性在被赋予特定的值之前能有一个默认的初始值nil。

    if let twoSocks = CartItem(name: "sock", quantity: 2) {
println("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
// 打印 "Item: sock, quantity: 2"

    if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
println("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
println("Unable to initialize zero shirts")
}
// 打印 "Unable to initialize zero shirts"

    if let oneUnnamed = CartItem(name: "", quantity: 1) {
println("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
println("Unable to initialize one unnamed product")
}
// 打印 "Unable to initialize one unnamed product"

### 覆盖一个可失败构造器

    class Document {
var name: String?
// 该构造器构建了一个name属性值为nil的document对象
init() {}
// 该构造器构建了一个name属性值为非空字符串的document对象
init?(name: String) {
if name.isEmpty { return nil }
self.name = 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
}
}
}

AutomaticallyNamedDocument用一个非可失败构造器init(name:),覆盖了基类的可失败构造器init?(name:)。因为子类用不同的方法处理了name属性的值为一个空字符串的这种情况。所以子类将不再需要一个可失败的构造器。

## 必要构造器

    class SomeClass {
required init() {
// 在这里添加该必要构造器的实现代码
}
}

    class SomeSubclass: SomeClass {
required init() {
// 在这里添加子类必要构造器的实现代码
}
}

## 通过闭包和函数来设置属性的默认值

    class SomeClass {
let someProperty: SomeType = {
// 在这个闭包中给 someProperty 创建一个默认值
// someValue 必须和 SomeType 类型相同
return someValue
}()
}

boardColor数组是通过一个闭包来初始化和组装颜色值的：

    struct Checkerboard {
let boardColors: [Bool] = {
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...10 {
for j in 1...10 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoard
}()
func squareIsBlackAtRow(row: Int, column: Int) -> Bool {
return boardColors[(row * 10) + column]
}
}

    let board = Checkerboard()
println(board.squareIsBlackAtRow(0, column: 1))
// 输出 "true"
println(board.squareIsBlackAtRow(9, column: 9))
// 输出 "false"