Swift 2.0 の initializer 自分用まとめ

https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html を適当におぼえるためにまとめただけ。

基礎

  • すべての property はインスタンス化に初期化されていないといけない

// 初期値は declaration と initializer の中どちらでやってもOK
struct Fahrenheit {
    var temperature = 32.0
}

struct Fahrenheit {
    var temperature: Double
    init() {
        temperature = 32.0
    }
}
// initializer はパラメータをとれる
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
// 初期化のパラメータは local name (initializer body で参照) と external name (呼び出し時に使う) がある
// fromFahrenheit が external name, fahrenheit が local name
    init(fromFahrenheit fahrenheit: Double) {
// external name が指定されなかったら自動で付加される
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
    }
}
// external name を使わずにインスタンス化できるようにしたいなら _ を使う
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
// property がそもそも値を持たないことが想定される場合
// 初期化時に値が決まっていない場合
// などには Optional Property を使う
class SurveyQuestion {
    var text: String
    var response: String? // こいつが Optional
    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."
// 定数プロパティの初期値は initializer のなかで設定できる
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.)"
// すべての property にデフォルト値が設定されている場合は
// 自動で initializer が生成される
class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()
// コードの重複を避けるため initalizer が他の initializer を呼ぶことができる
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) // これ
    }
}
// designated initializer はクラスの初期化に使われるメインの initializer 。すべての property を初期化したり適切な super クラスの initializer を呼ぶ
// クラスはあまり多くの designated initializer を持たない。1つの場合が多い。
// クラスは必ず1つの designated initializer を持たなければならない。継承で得る場合も多い。
// designated initializer の例
init(parameters) {
    statements
}
// Convenience initializers は必須ではない
// 特定の使用目的のための initializer を提供するのに便利。その中から designated initializer を呼べば良い
convenience init(parameters) {
    statements
}

designated / convenience initializers のルール

  • Rule 1: designated initializer は1つ上の super class の designated initializer を呼ばないといけない
  • Rule 2: convenience initializer は同じクラスの initializer を呼ばないといけない
  • Rule 3: convenience initializer は最終的には designated initializer を呼ばないといけない

initializer の継承

  • default では super class からの initializer の継承は*行われない*
  • 上記によって child クラスがせっかく特殊用途のクラスなのに、super class の initializer が呼ばれる可能性があって台無し
  • 上記の理由により意図的に super class の designated initializer をつかうなら override キーワードを付けないけない

initializer が継承されるルール

  • Rule 1: サブクラスが1つも designated initializer がないばあい。自動的にすべての designated initializer を継承する
  • Rule 2: Rule 1 か、もしくは明示的に super class のすべての designated initializer を override している場合。自動的に convenience initializer も継承される

Failable Initializers

  • initializer が失敗する可能性があるクラスを定義できる
  • ? をつけることで initializer が option を返すことを明示できる
struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}

Required Initializers

required がついていたら sub class で実装しないチケ内

class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}