開発メモ

開発関係のメモをいろいろと。たぶん。

The Swift Programming Languageのメモ(その8)

はじめに

これは、『The Swift Programming Language』を読んだ時の個人的なメモです。なので、引用はすべて『The Swift Programming Language』から。

iTunes - ブック - Apple Inc.「The Swift Programming Language」
https://itunes.apple.com/jp/book/swift-programming-language/id881256329?mt=11

その7はこちら。

The Swift Programming Languageのメモ(その7) - 開発メモ
http://seeku.hateblo.jp/entry/2014/06/11/192313


Initialization

  • Objective-Cのイニシャライザーと違って、Swiftのイニシャライザーは値を返さないよ

Setting Initial Values for Stored Properties

  • クラスや構造体は、インスタンスを生成する時点で値を格納するプロパティをすべて初期化しておく必要があるよ

Initializers

  • イニシャライザーは『init』で定義するよ
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

Default Property Values

  • プロパティを宣言する時点でデフォルト値を指定しておくと、初期化で使われるよ

Initialization Parameters

  • イニシャライザーに引数を渡すことも出来るよ
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

Local and External Parameter Names

  • イニシャライザーの引数名は全て、自動的に中と外で使われることになるよ
    メモ: イニシャライザーだけ特別扱い

メモ: どうして、こんなにわかりにくい仕様になってるのか?

  • 引数名を外で使いたくない時は、『_』で捨てることが出来るよ

Modifying Constant Properties During Initialization

  • イニシャライザーの中では定数プロパティ(let)を設定することが出来るよ

  • NOTE: サブクラスのイニシャライザーの中で、元のクラスの定数を変更することは出来ないよ

Default Initializers

  • Swiftはデフォルトのイニシャライザーを提供するよ
  • これは、すべてのプロパティがデフォルト値を持ってて、他にイニシャライザーが無い場合に使えるよ
  • デフォルトイニシャライザーは、すべてのプロパティをデフォルト値にするだけだよ
class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()

Memberwise Initializers for Structure Types

  • 構造体には、メンバー指定イニシャライザーが提供されるよ
  • プロパティの名前と値を指定して使うイニシャライザーだよ
  • こんな感じになるよ
struct Size {
    var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)

Initializer Delegation for Value Types

  • イニシャライザーから他のイニシャライザーを呼ぶことが出来るよ
  • この部分の動作は、値型とクラスで違うよ
  • 値型(構造体と列挙型)は継承がないから、凄くシンプルだよ
    自分自身の他のイニシャライザーを呼べばいいだけだよ。

  • けど、クラスの場合は継承があるからいろいろ面倒だよ

Class Inheritance and Initialization

  • 値を格納するプロパティは、イニシャライザーですべて初期化する必要があるよ
  • このことを保証するために、Swiftには二種類のイニシャライザーがあるよ
    指定イニシャライザーと便利イニシャライザー(?)だよ。

Designated Initializers and Convenience Initializers

  • 指定イニシャライザーは、いわゆる普通のイニシャライザーだよ
  • すべてのプロパティを初期化するよ
  • スーパークラスのイニシャライザーを呼び出すよ
  • 指定イニシャライザーはできるだけ少ないほうが良いよ
    ほとんどのクラスでは1つだよ。
  • 最低でも1つは必要だよ

  • 便利イニシャライザーは初期化を補助するイニシャライザーだよ

  • 同じクラスの指定イニシャライザーを呼び出すよ
  • 便利イニシャライザーは無くても良いよ

メモ: イニシャライザー周りの処理はわかりにくいので、自力でいろいろ確認した方が良さそう

Initializer Inheritance and Overriding

  • Objective-Cと違って、イニシャライザーは自動で継承されないよ
    これは、スーパークラスのシンプルなイニシャライザーが勝手に継承されて、サブクラスで初期化されないプロパティが出るのを防ぐためだよ。

  • 自動で継承されないから、自力でオーバーライドすればいいんだよ

  • 指定イニシャライザーをオーバーライドした時は、スーパークラスのイニシャライザーを呼び出すよ
  • 便利イニシャライザーをオーバーライドした時は、同じクラスの指定イニシャライザーを呼び出すよ
  • イニシャライザーをオーバーライドする時は『override』を付ける必要は無いよ

Automatic Initializer Inheritance

  • 実は、イニシャライザーが自動で継承される場合もあるよ
  • サブクラスで追加したプロパティがすべてデフォルト値を持つ場合、次の2つのルールが適用されるよ
    1. サブクラスが指定イニシャライザーを持たない場合、自動的に指定イニシャライザーが継承されるよ
    2. すべての指定イニシャライザーをオーバーライドしてる場合、自動的に便利イニシャライザーが継承されるよ

メモ: よく考えると便利なんだけど、わかりにくすぎない?

Syntax for Designated and Convenience Initializers

  • 指定イニシャライザーはこんな感じになるよ
init(parameters) {
    statements
}
  • 便利イニシャライザーはこんな感じになるよ
convenience init(parameters) {
    statements
}

Setting a Default Property Value with a Closure or Function

  • 値を格納するタイプのプロパティは、デフォルト値を関数やクロージャで設定することも出来るよ

  • 具体的にはこんな感じになるよ

class SomeClass {
    let someProperty: SomeType = {
        // create a default value for someProperty inside this closure
        // someValue must be of the same type as SomeType
        return someValue
        }()
}
  • クロージャの最後の『()』が大事だよ
    これがないと、クロージャがプロパティの値になっちゃうよ

  • NOTE: プロパティの初期化にクロージャを使う場合、インスタンスの他の部分はまだ初期化されてない事に注意が必要だよ。つまり、他のプロパティにはアクセス出来ないよ。デフォルト値を持ってるプロパティも駄目だよ。暗黙のselfも使えないし、他のインスタンスメソッドも呼び出せないよ。

メモ: これだったら、普通にイニシャライザーで初期化したほうが楽では?

Deinitialization

メモ: Deinitializationに対応する日本語は? 逆イニシャライザー?

  • deinitはインスタンスが破棄される直前に呼び出されるよ
  • deinitはクラスだけが使えるよ

How Deinitialization Works

  • 具体的にはこんな感じになるよ
deinit {
    // perform the deinitialization
}

Automatic Reference Counting

メモ: Objective-Cの時代と大差なし

Unowned References

  • weak参照と同じように、unownedもインスタンスを保持しないよ
  • weak参照と違って、対象がnilになることは無いよ
  • もちろん、解放後の参照にアクセスするとランタイムエラーになるよ

Strong Reference Cycles for Closures

  • selfを使うクロージャを自分自身のプロパティに持たせると、それだけでstrong参照の循環が出来ちゃうよ
  • この問題を解決するためのエレガントな方法として、Swiftにはクロージャキャプチャリストがあるよ!
    メモ: そんなにエレガントだろうか?

Resolving Strong Reference Cycles for Closures

  • クロージャキャプチャリストは、参照型をキャプチャする時のルールを定義したものだよ

Defining a Capture List

lazy var someClosure: (Int, String) -> String = {
    [unowned self] (index: Int, stringToProcess: String) -> String in
    // closure body goes here
}
  • いろいろ省略した場合はこんな感じになるよ
lazy var someClosure: () -> String = {
    [unowned self] in
    // closure body goes here
}

メモ: 素直に、weakSelfとかunownedSelfを用意したほうが良いのでは? 言語として


とりあえずここまで。
(2014/06/12 作成)
(2014/09/25 修正)