My Octopress Blog

A blogging framework for hackers.

Swift Initialization

swift的初始化

这里不是要权威的解释initilization,而是想总结一些swift initilizer的特点

初始化发生在class, structure, enumeration初始准备的过程中。coder可以通过定义initializers来实现初始化。值得注意的是,initializers没有返回值。

此外,class还可以实现一个’deinitializer’

如何定义initializer

定义initializer需要用关键字init

Simplest initializer

struct Fahrenhit{
    var temperature: Double
    init(){
        temperature = 32.0
    }
}

自定义initializer

可以使用initializer parameters来自定义initializer

struct Celsius{
    var temperatureInCelsius: Double
    init(fromFahrenhit fahrenhit: Double){
        temperatureInCelsius = (fahrenhit - 32.0)/1.8
    }
    init(fromKelvin kelvin: Double){
        temperatureInCelsius = kelvin - 273.15
    }
}

调用initializer

由于initializer每个参数默认有external name,所以调用的时候一定要写external name

let boilingPoint = Celsius(fromFahrenhit: 212.0)
let freezePoint = Celsius(fromKelvin: 273.15)

Default initializer

  • 如果structure或者base class没有提供initializer的话,编译器会提供一个默认的init()。

  • 对于structure来说如果没有任何自定义的initializer的话,编译器会还会提供一个memberwise initializer

Initializer Delegation

  • value类型和class类型的initilizer delegation的规则是不同的

Structure和Enumunation的Intializer Delegation

  • 只能在initializer中调用self.init

Class Inheriantance and Initialization

class的initializer分为Designated initializer和Convenience initializer

Designated initializer和Convenience initializer

Designated initializer是primary initializer,每个class只要要有一个

init(parameters) { statements }

Convenience initializer是为了方便而存在的,需要通过Designated initializer来初始化,关键字为convenience

convenience init(parameters){ statements }

Initializer Delegattion for Class Types

  • Rule1 一个designated initializer必须调用它直接父类的designated initializer
  • Rule2 一个convenience initializer必须调用它自己的另一个initializer
  • Rule3 一个convenience initializer必须最终调用它自己的designated initializer

可以简单总结为

  • designated initializer必须delegate up
  • convenience initializer必须delegate across

可以看下图

下面是一个更复杂的例子

两阶段的初始化

在初始化过程中,首先所有的类属性都会被赋予初始值,当这一阶段完成之后,然后在被initializer里代码设置其值
关于这点,swift的编译器会进行一些检查

  • safety check 1 : designated initializer在delegates up到superclass initializer之前,必须保证所有属性都被初始化了

  • safety check 2 : designated initializer必须先delegate up到superclass initializer才能赋值给继承下来的属性

  • safety check 3 : convenience initializer必须先delegate到其他initializer才能修改属性

  • safety check 4 : initializer需要第一阶段初始化完成才能修改属性,调用成员方法

两阶段初始化的具体过程不详细说了,需要知道的是,当内存分配好之后,先由子类的initializer负责初始化自己的属性,然后向上传递控制权,父类做相同的事情,直到根基类。这时候第一阶段初始化就完成了。第二阶段初始化是从顶向下进行,这时候self就可以访问了,同时可以修改访问self的属性了,调用成员函数了。

第一阶段初始化

第二阶段初始化

Initializer的Inheritance和Override

原则上,swift不允许initializer继承,但是在下列情况下可以继承

  • 如果子类没有写任何designated initializer,子类可以继承所有父类的designated initializer

  • 如果子类提供了所有父类的designated initializer的实现--无论是继承而来的,还是自己override的,那么自动继承所有的父类的convenience initializer

下面举个例子:

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

下图显示了initializer chain

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

initializer chain如图

class ShoppingListItem: RecipeIngredient {
    var purchased = false
    var description: String {
        var output = "\(quantity) x \(name)"
        output += purchased ? "OK" : "Not"
        return output
    }
}

initializer chain如图

Failable Initializer

语法如下

init?(parameters){ statments }

举个例子来说 struct Animal{ let species: String init?(species: String){ if species.isEmpty { return nil } self.species = species } }

通过return nil,我们可以表示初始化失败

可以像如下方式来使用 let someCreature = Animal(species: “”) if let griaffe = someCreature{ println(“no species”) }

Failable Initializer for Enumerations

enum TemperatureUnit{
    case Kelvin, Celsius, Fahrenheit
    init?(symbol: Character){
        switch symbol{
        case "K": self = .Kelvin
        case "C": self = .Celsius
        case "K": self = .Fahrenheit
        default:
            return nil
        }
    }
}


if let unknowUnit = TemperatureUnit("X") { println("no such unit") }

Automatic Failable Initializer for Enumerations with Raw Value

enum TemperatureUnit : Character{
    case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
}

if let unknowUnit = TemperatureUnit("D"){ println ("no such unit") }

Failable Initializer for Classes & Propagation of Initialization Failure

对于类来说,初始化失败只能在所有属性都已经被设置了初始值之后发生

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

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

Override a Failable Initializer

你可以使用non-failable initializer去override父类的failable initializer,但这时就不能再delegate up到failable initilizer

class Document{
    var name: String?
    init() {}
    init?(name: String){
        self.name = name
        if name.isEmpty { return nil }
    }
}

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

Required Initializer

required modifier表示该类的每一个子类都必须实现这个initilizer
在子类实现该initializer时候必须也添加required

class SomeClass{
    required init(){ ... }
}

class SomeSubClass{
    required init(){ ... }
}

Setting a Default Property Value with a Closure or Function

class SomeClass{
    let someProperty: SomeType = {
        return someValue
    }()
}