Menu

Category

Archive

logo


The Swift Programming Language ~ Initialization

2014-06-20 22:00:00 +0900
  • このエントリーをはてなブックマークに追加

iBooks にある “The Swift Programming Language” の勉強メモ。Objective-C と C を普段書いている自分から、ちょっと馴染みがないものを特にまとめておきます。目次は こちら

今回は、Initialization に関して。

Initialization

初期化は、クラス・構造体・Enumeration のインスタンスを使用するための準備です。この初期化の際に Stored プロパティの初期値をセットやそれらに関連した処理を行います。

この初期化を実装するために、Initizliser を定義します。これは特定にタイプに対して、そのインスタンスが作成される際に呼ばれる特殊なメソッドです。Objective-C と違い、Swift の Initializer は返り値を持ちません。

1. 初期値の設定

クラスと構造体の Stored プロパティはインスタンスが作成される際に初期値を必ず持たなければなりません。Stored プロパティ は、不確定な状態にしておくことはできません。この初期値は、Initializer の中で設定できますし、プロパティにデフォルト値を持たせることでも実装できます。

Initializer は init キーワードを使用し、引数を持たないインスタンスメソッドのように記述します。

1
2
3
4
5
6
7
8
9
struct Fahrenheit {
    var temperature: Double
    init() {
        temperature = 32.0
    }
}
var f = Fahrenheit()
println("The default temperature is \(f.temperature)° Fahrenheit")
// prints "The default temperature is 32.0° Fahrenheit

しかし、いつも同じ初期値を取る場合には、Initializer を使用するより、プロパティにデフォルト値を持たせる方が得策です。こちらの方が、Swift の型推論を使用し記述が簡単ですし、デフォルト Initializer や Initializer を継承させる場合に便利です。

1
2
3
struct Fahrenheit {
    var temperature = 32.0
}

2. カスタム Initializer

Initializer に引数や Optional property types を持たせたり、 コンスタントプロパティを修正したりと、独自の Initializer を実装することができます。

まずは、Initializer に引数を持たせる例です。関数やメソッドと同じように使用できます。下記の例では、2 つの Initializer を定義し、どちらも引数を取っています。また、External な引数の名前も持っています。

1
2
3
4
5
6
7
8
9
10
11
12
13
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 is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0

このように Initializer は引数の外的な名前と Initializer の中で使う内的な名前を両方持つことができます。これはメソッドも関数と同じです。しかし、全ての Initializer は init と同じ名前を持つので、どの Initializer が呼ばれるかを決定するのに引数や引数の型は重要な意味を持ちます。そのため、Swift は全ての Initializer に対して、外的な名前を作成します。これは、内的な名前と同じです。つまり、# を引数の前に記述した場合と同じです。もし、外的な名前を用意したくない場合には、_ を使用します。

1
2
3
4
5
6
7
8
9
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)

このように Initializer を呼ぶ出す際に、全ての引数に対して外的な引数名を指定しなければなりません。

Stored プロパティが Optional な場合は、自動的に nil が代入されるため、Initializer で処理を記述する必要はありません。

また、コンスタントのプロパティは、初期化の際であれば修正することができます。クラスの場合には、そのプロパティを持っている該当クラスでのみ変更可能です。サブクラスの Initializer の中では変更ができません。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class SurveyQuestion {
    let text: String
    var response: String?
    init(text: String) {
        self.text = text
    }
    func ask() {
        println(text)
    }
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// prints "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)

このようにコンスタントで宣言されている text プロパティに対しても、初期化時なら値を変更できます。

3. Default Initializers

Swift は、デフォルトの Initializer を作成することがあります。構造体かベースクラスにおいて、全てのプロパティに対してデフォルト値が設定され、Initializer が提供されていない場合に作成されます。このデフォルト Initializer はこのプロパティのデフォルト値を使用したインスタンスを作成してくれます。

1
2
3
4
5
6
class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem() // default initializer

同じ デフォルトで作成される Initializer に構造体の Initializer があります。

1
2
3
4
struct Size {
    var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0) // default memberwise initializer

上記の Size 構造体のようにプロパティの全てにデフォルト値が設定され、Initializer が定義されていない場合には、このように memberwise initializer が作成されます。各プロパティ名が外的な引数の名前になります。

4. 構造体・Enumeration の Initializer delegation

Initializer は他の Initializer を使うことができます。この delegation により、複数の Initializer が同じコードを持つことを防ぐことができます。タイプでも継承をサポートしているクラスとサポートしていない構造体・Enumeration ではこの Initializer の動作に違いがあります。ここでは、構造体と Enumeration に関してみていきます。

構造体・Enumeration のようなバリュータイプでは、同じタイプの中にある Initializer を self.init で呼び出し、独自の Initializer を作成できます。ここで注意しておきたいのが、独自の Initializer を定義した場合、3 でみた Default Initializer は使うことができなくなります。この制約により、インスタンスを作成する際に大事なセットアップが必要な場合、他の誰かが Default Initializer を利用して、大事なセットアップを回避してまうことを防ぐことがきます。Default Initializer と Memberwise Initializer で初期化させたい場合には、Extension という仕組みがあります。Extension に関しては、まだ勉強できていないので次回以降に記事にしたいと思います。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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)
    }
}

上記の Rect の Initializer の 1 つ、init(center:size:) が独自の Initializer で処理の一部を init(oriting:size:) に Delegate しています。