Generics

Genericsは鬼門だわ

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

  • genericな関数 func swapTwoValues<T>(_ a: inout T, _ b: inout T) {…}
  • 使うときは swapTwoValues(&someValue, &anotherValue) 適切に型推論される
  • 実際にはSwift標準ライブラリの swap(_:_:) を使うが良い
  • 総称型 struct Stack<Element> {…}
  • 総称型へのextension extension Stack {…}
  • 型の制約 func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {…}
  • func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {…}
  • プロトコルは総称型にできないので、替わりに associatedtype を使う
  • プロトコルをconformする実装では typealias 抽象型 = 具体型
  • 型推論がうまくいけば typealias はなくてもいける
  • 既存の型へのextensionでプロトコルをconformするとき、関連する型も含まれる
  • プロトコルで関連する型の制約 associatedtype Item: Equatable
  • プロトコルで関連する型の制約 associatedtype T: U where T.Item == Self.Item
  • generic関数で制約 func f<C1: C, C2: C> (_ a: C1, _ b: C2) -> Bool where C1.Item == C2.Item, C1.Item: Equatable {}
  • 総称型へのextensionで型パラメータの制約 extension Stack where Element: Equatable {…}
  • プロトコルへのextensionで関連型の制約 extension Container where Item == Double {…}
    • プロコトルへのextension中のsubscriptにwhere句 subscript<Indices: Sequence>(indices: Indices) -> [Item] where Indices.Iterator.Element == Int {…}

protocolを既存の型にextentionで適合させるときに 思いもよらない組み合わせを生み出しそうで恐ろしい。

たぶん自分ではそんなに凝ったことは書かないと思うので問題はないけど 他人が書いたコードを読める自信は無い。

Protocols

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

  • protocol は 他の宣言の内側にネストできない
  • extension と同じファイルスコープ、なのにエラーメッセージが違うのはなぜだ
  • プロパティはインスタンスと型を要求できる。格納型か計算型かどうかは指定できない。名前と型だけ
  • 読み取り専用か、読み書き可能かは指定できる
  • プロトコルが読み書き可能を要件したら、定数格納プロパティや読み取り専用計算プロパティでは要件を満たせない
  • 読み取り専用なら、どんな形式のプロパティでも満たせる
  • プロトコルではvarキーワード 読み書き可能は { get set } 読み取り専用は { get }
  • 型プロパティ・型メソッドはプロトコルでは static でも、クラスでは class 宣言できる
  • こんなところで "John Appleseed"
  • インスタンスメソッドと型メソッドを要求できる
  • 可変引数できる。パラメータのデフォルト値は、プロトコルの定義ではできない
  • プロトコルmutating メソッドは構造体と列挙型でのみ使われる クラスの実装では mutating 不要
  • 指定イニシャライザ または 便利イニシャライザ のどちらも
  • プロトコルでイニシャライザを要求したら、実装では required 修飾子をつけて required init(〜) {…}
  • final クラスでは required 修飾子は要らない。なぜなら finalクラスはサブクラス化できないから
  • プロトコルでfailableなイニシャライザを要求したら、実装はfailableでもnonfailableでもどっちでも要件を満たす
  • プロトコルでnonfailableなイニシャライザを要求したら、実装はnonfailableなイニシャライザか、暗黙にunwrapするfailableなイニシャライザが要件を満たす
  • プロトコルで要件したイニシャライザを実装したクラスから派生したサブクラスでoverrideしたら required override init(〜)
  • delegateなパターンは弱参照で weak var delegate: XxxxDelegate?
  • プロトコルコンポジション ProtocolA & ProtocolB
  • プロトコルをconformしてるかどうかは is キャストは as?as!
  • プロトコル定義でオプショナルな要求は optional 修飾子をつけて
  • オプショナルな要求は Objective-C との相互運用でavailableなので @objc 属性をつけて
  • オプショナルな要求は 構造体と列挙型には適合できない
  • オプショナルな要求は Optionalな型になるので、使うときは ? をつけてopttional-chaining
  • 型のextensionで、型にprotocolをconform
  • プロトコルのextensionで、メソッドをデフォルト実装
  • プロトコルのextensionで、制約 extension Collection where Element: Equatable {…}

Extensions

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

  • オリジナルソースコードにアクセスできなくても機能性を追加できる(遡及モデリング)
  • extension を宣言できるのはファイルスコープだけ
  • extensionでプロトコル適合は Protocol の章で
  • 総称型の extension は Generics の章で
  • let aMarathon = 42.km + 195.m
  • extensionで便利イニシャライザを追加できるけど、指定イニシャライザや deinitializer は追加できない
  • 別のモジュールで宣言された構造体に、エクステンションでイニシャライザを追加すると、新しいイニシャライザは定義モジュールのイニシャライザを呼び出すまで、selfにアクセスできません。
  • 3.repetitions { print("Hello!") }
  • mutating func square() { self = self * self }
  • extensionにネストされた型 extension Int { enum Kind {〜} …}
  • ネストされた型へのextension extension OuterType.InnerType {〜}

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

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