Nested Types

昨日に引き続き https://docs.swift.org/swift-book/LanguageGuide/NestedTypes.html を読む。

  • Swiftでは型の入れ子ができる
  • 特定の型の中だけで使う列挙型とか、そいういう目的で使う
  • 列挙型、クラス、構造体のどれでも
  • 型は必要なだけいくらでも深くネストできる
  • 一番外側の型名.ネストされた型名.更にネストされた型名.…
  • 文脈から、型推論によって、ネストされた型名を全部書かなくても済む

Type Casting

昨日に引き続き https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html を読む。

  • Swiftの型キャストは isas演算子
  • protocolをconformしているかどうかにも使える
  • 型検査演算子 is item is Movie
  • 「subclassかどうか」だけど構造体・列挙型でも使える
  • ダウンキャスト演算子 as?as!
  • ダウンキャストは失敗するかもしれないので2つある
  • ダウンキャストが成功するのがnot sureなときは as?を使え
  • ダウンキャストが成功するのがsureなときは as!を使え
  • “if let movie = item as? Movie” は、「itemMovieとしてアクセスしてみて、うまくいったらmovie一時変数にオプショナル格納されてる値を入れてよね」と読む
  • Anyには全ての型のインスタンスが入る。タプルや関数も入る
  • AnyObjectに入るのはクラスのインスタンスだけ
  • switchcaseで型検査する例
    • case 0 as Int:
    • case let i as Int:
    • case let x as Double where x > 0:
    • case let (x, y) as (Double, Double):
    • case let f as (String) -> String:
  • AnyにOptionalを入れるには、明にキャストしないと警告になる let o:Any = Int("123") as Any

intをdoubleにするようなキャストではない

Error Handling

昨日に引き続き https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html を読む。

  • Swiftでは、エラーは、Errorプロトコルに準拠した型の値として表される
  • Swiftの列挙型はエラー状態に関連したグループをモデル化するのに特に適している
  • Swiftではエラーハンドルの方法は4つある
    1. エラーを伝播させる
    2. docatch 構文でハンドリング
    3. オプショナル値として
    4. エラーは起きないものとして
  • Swiftのエラーハンドリングはコールスタックの巻き戻しはしない
    • throw文の性能特性は、return文のと同じくらい
  • エラーを投げれば関数はエラーを伝播できる
  • エラーを投げない関数の内側では、エラーをハンドリングしなければならない
  • guardステートメントでメソッドを早く抜ける
  • エラーを投げる関数を呼ぶには try キーワードを前につけて
  • do節でエラーが投げられるかもしれない関数を呼んで、catch節でどのエラーに対するか決定する
  • catch のパターンは case みたいな感じで
  • 最後のcatch節ではエラーはローカル変数errorに束縛される
  • 列挙型でキャッチ catch is VendingMachineError
  • try? 式でオプショナル値に変換 let x = try? someThrowingFunction()
  • try! 式で、エラーは起きないものという実行時 assertionにしてしまう let image = try! loadImage(〜)
  • defer で現在のスコープが終わるまで実行を後回しする
  • deferred文では、ステートメント外へ制御を移すberakreturnthrowはできない
  • 同一スコープで複数 defer を書いたら、ソースコード順で後のやつから順に呼び出される
  • エラー処理じゃなくても defer 構文は使える

最近Web上のSwiftの記事なんかを見て 「読める…読めるぞ!」という雰囲気になってきたので 学習高原に達した気がする。

Optional Chaining

昨日に引き続き https://docs.swift.org/swift-book/LanguageGuide/OptionalChaining.html を読む。

  • 強制アンラッピングの代替としてのオプショナル連鎖
  • オプショナル値の後に ? をつけて
  • オプショナル値がnilのとき、オプショナル連鎖は優雅に失敗する、 強制アンラッピングはランタイムエラー
  • プロパティ等の返り値が非オプショナルであっても、オプショナル連鎖の返り値はいつもオプショナル
  • オプショナル連鎖経由で非オプショナルのプロパティを読み取り成功したことは オプショナル束縛で判る
    • if let x = o.property?.subProperty {
  • オプショナル連鎖経由でVoidのメソッドはVoid?を返す
    • 呼んだかどうか検査するには if o.property?.method() != nil
    • プロパティ値設定も同じ if (o.property?.subProperty = value) != nil
  • 添字形式は a?[x] table[i]?[j]
  • オプショナル値を返すメソッドが混ざっても if let x = o.p1?.p2?.m1()?.m2()

「オプショナル値の後ろに?」 であって「?. 演算子ではない」、という解釈

Deinitialization

今日のは少ない

昨日に引き続き https://docs.swift.org/swift-book/LanguageGuide/Deinitialization.html を読む。

  • Swiftのメモリ管理はARC
  • deinitializer はクラスのインスタンスがメモリから解除される直前に呼び出される
  • deinit キーワードをつかってinitみたいに、 パラメータも括弧()もなし deinit {〜}
  • ひとつのクラスに最大でひとつの deinitializer
  • インスタンスの割り当て解除が行われる直前に、自動的にdeinitializerが呼び出される
  • deinitializer の実装はインスタンスのプロパティ全てにアクセスできる
  • あなたは自分自身でdeinitializerを呼び出すことはできません
  • スーパークラスdeinitializerはサブクラスによって継承される
  • サブクラスdeinitializer→スーパークラスdeinitializerの順に呼び出される
  • スーパークラスdeinitializerはサブクラスが独自のdeinitializerを提供しない場合でも常に呼び出される
  • クラスのインスタンスが入ったOptionalな変数にnilを代入すれば deinitializer が呼び出される

LANGUAGE GUIDEの半分は過ぎたみたい

Initialization

訳はてきとう

前回に引き続き https://docs.swift.org/swift-book/LanguageGuide/Initialization.html を読む。

  • 格納型プロパティは、初期化子のなかで値を設定するか、プロパティの宣言に初期値を書く
  • 初期化子の中で値を設定するか、初期値を宣言したときは、プロパティオブザーバは呼び出されない
  • プロパティがいつも同じ初期値をとるなら、初期化子内で設定するよりも初期値を与えよ
  • クラスの格納型プロパティにすべて初期値があればデフォルト初期化子がある
  • 構造体には全項目初期化子がある
  • 初期化子の委譲は self.init
  • 指定初期化子と便利初期化子
  • クラスの指定初期化子は、少ない傾向。ひとつしか持たないことがよくある
  • どのクラスも1つは指定初期化子を持たなければならない
  • 便利初期化子 convenience 修飾子を init キーワードの前につけて
  • 委譲のルール
    1. 指定初期化子は、スーパークラスの指定初期化子を呼び出さなければならない
    2. 便利初期化子は、同じクラスの他の初期化子を呼び出さなければならない
    3. 便利初期化子は、最終的には指定初期化子を呼び出さなければならない
  • 単純版
    1. 指定初期化子は上へ委譲する
    2. 便利初期化子は横へ委譲する
  • Swiftでは、クラスの初期化は2段階
  • 2段階初期化プロセスを使用すると、クラス階層内の各クラスに完全な柔軟性を与えながら、初期化を安全に行うことができる
  • 2段階初期化は、プロパティ値が初期化される前にアクセスされるのを防ぎ、予期せず別の初期化子によってプロパティ値が別の値に設定されるのを防ぐ
  • 安全性検査
    1. 指定初期化子は、スーパークラスの初期化子に委譲する前に、そのクラスによって導入されたすべてのプロパティが初期化されていることを保証する必要がある
    2. 指定初期化子は、継承されたプロパティに値を割り当てる前に、スーパークラスの初期化子に委譲する必要がある
    3. 便利初期化子は、プロパティ(同じクラスで定義されたプロパティを含む)に値を割り当てる前に、別の初期化子に委譲する必要がある
    4. 初期化子は、初期化の第1段階が完了するまで、インスタンスメソッドを呼び出したり、インスタンスプロパティの値を読み込んだり、selfを値として参照したりすることはできない
  • 2つの段階
    • 第1段階
      • クラスの初期化子が呼び出される
      • 新しいインスタンスのためのメモリが確保される(未だ初期化されてない)
      • 指定初期化子がクラスのプロパティに値が格納されるのを確実にする(このクラスのプロパティは初期化済み)
      • スーパークラスの初期化子へ、継承チェインの先頭まで
      • 継承チェインの先頭まで来たら、全部の格納プロパティに値が入ってメモリは初期化済み(第1段階終了)
    • 第2段階
      • 継承チェインの先頭から戻りながら、指定初期化子にはさらにカスタマイズする選択肢がある
      • 初期化子は、この段階では、selfにアクセスしたりプロパティ値を変更したり、メソッドを呼んだりできる
      • 最後に、便利初期化子にはカスタマイズする選択肢がある
  • Objective-Cのサブクラスとは異なり、Swiftサブクラスはデフォルトでスーパークラス初期化子を継承しない
  • 自動的に継承される初期化子。サブクラスで提供するプロパティにすべて初期値が宣言されているとして、
    • ルール1 サブクラスで指定初期化子を何も定義しないなら、スーパークラスの指定初期化子をすべて継承する
    • ルール2 サブクラスがそのスーパークラスの指定初期化子をすべて継承しているなら、スーパークラスの便利初期化子をすべて継承する
  • 失敗可能初期化子 init?
  • 失敗時はnilreturnするけど、成功時はとくになにもreturnしないよ
  • 同じ名前と型のパラメータで失敗可能・失敗不能初期化子を両方定義することはできない
  • rawValueのあるenumは自動的に init?(rawValue:) を収容している
  • 失敗可能を失敗不能にオーバーライドできるけど、逆は無理
  • 初期化中に強制アンラップしている失敗可能な初期化子は init!
  • サブクラスで実装しなければならない初期化子は required init()
  • クロージャでプロパティの初期化 let someProperty: SomeType = { return someValue }()

やたら量が多いので、他の章の5倍はあるけど、内容はまあ、ゾッともゲッともしない。ああそうだね、という感じ。 スーパークラスの初期化が終わってないのにスーパークラスのメソッドが呼び出せたりするような危険な状況を回避するために、 厳格で単純なルールを作っている、…と思った(夏休みの感想)。

Inheritance

前回に引き続き https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html を読む。

  • 何からも継承してないクラスはベースクラス
  • Swiftのクラスはユニバーサル基底クラスからの継承ってのは無い
  • クラスメソッド、インスタンスメソッド、クラスプロパティ、インスタンスプロパティ、添字はオーバーライドできる
    • static funcstatic var はオーバーライドできない
  • オーバーライドする側で override キーワードをつける…誤ってオーバーライドすると予期しない振る舞いをするから
  • サブクラスでオーバーライドした実装中でスーパークラス版にアクセスするには super prefixをつける
    • super.someMethod(), super.someProperty, super[someIndex]
  • オーバーライドするプロパティの名前と型の両方を常に記述する必要がある
  • スーパークラスで読み取り専用だったプロパティを、サブクラスで読み書き可能にできる
  • スーパークラスで読み書き可能だったプロパティを、サブクラスで読み取り専用にはできない=>コンパイルエラー
  • プロパティをオーバーライドしてsetterを提供するときは、getterも提供しなきゃいけない
  • プロパティをオーバーライドして、オブザーバーを追加できる
  • willSet(sub)→willSet(super)→didSet(super)→didSet(sub) の順に呼ばれる様子
  • 定数格納型プロパティ、読み取り専用計算型プロパティは、オブザーバーを追加できない
  • 同じプロパティに対して、オーバーライドセッターとオーバーライドプロパティオブザーバーの両方を指定することはできない
    • カスタムセッターの中で値変更を監視すればよい
  • オーバーライドを禁止するのは final 修飾子
    • final var, final func, final class func, final subscript
  • サブクラス化を禁止するのは final class

self は property で super は prefix、 final は modifier、 override は keyword