Advanced Operators

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

  • Swiftの算術演算子はオーバーフローしない
  • Int16.max + 1 はエラー
  • オーバーフローする演算子
    • オーバーフロー 加算 &+
    • オーバーフロー 減算 &-
    • オーバーフロー 乗算 &*
  • 二項 static func + (left: T, right: T) -> T {…}
  • 単項前置 static prefix func - (v: T) -> T {…}
  • 複合代入 static func += (left: inout T, right: T) {…}
  • デフォルトの代入演算子オーバーロードできない
  • 3項演算子オーバーロードできない
  • 等価 static func == (left: T, right: T) -> Bool {…} プロトコルEquatableをconform
  • ==を実装したら、ふつう != は実装する必要はない
  • Equatableプロトコルをconformすると宣言したら Swift が==を合成してくれる場合
    • Equatable型の格納プロパティだけ持ってる構造体
    • Equatable型の関連型だけ持つ列挙型
    • 関連型を持たない列挙型
  • カスタム演算子 operator 宣言は ファイルスコープでなければならない
  • prefix operator ☹
  • static prefix func ☹ (v: T) -> T {…}
  • infix operator ☺: AdditionPrecedence
  • static func ☺ (left: T, right: T) -> T {…}
  • 演算子の優先順位は Swift Standard Libraryのほうで決まっているみたい

さてLANGUAGE GUIDEがひととおり終わったので なんかiOSアプリを作ってみたい。ゲームとか。 LANGUAGE REFERENCEを読むのは重箱の隅ほど美味しそうだけど深みにハマっていくのもアレなので。

Access Control

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

  • アクセスレベルは5段階
enum AccessLevel {
    case `open`
    case `public`
    case `internal`
    case `fileprivate`
    case `private`
}
  • レベルの高低は、openが最高(最も制限が少ない)、privateは最低(最も制限が多い)
  • openpublicの違いはmoduleを越えてsubclass化とoverrideできるかどうか
  • エンティティは より低い(より制限的な)他のエンティティが出てくるようなやつは 定義できない
    • publicな変数は、internalfileprivate,privteな型で定義できない
    • 関数は、パラメータや返り値の型より高いアクセスレベルにできない
  • デフォルトのアクセスレベルはinternal
  • 多くの場合、コードに明示的なアクセスレベルを指定する必要はありません。
  • 単純な単一ターゲットアプリを書く場合は、internalで充分要件を満たしている
  • フレームワーク開発では APIopenpublicにする
  • 単体テスト可能なのは openpublicだけどinternalなのをテストしたいなら@testableアトリビュートを付ける
  • 型のアクセスレベルは、メンバーの暗黙のアクセスレベルになる
    • privateかfileprivateにすると、メンバーの暗黙のアクセスレベルはprivateかfileprivateになる
    • internalかpublicにすると、メンバーの暗黙のアクセスレベルはinternalになる
  • タプルのアクセスレベルは、中身から最も低いアクセスレベルのやつになる
  • 関数の暗黙のアクセスレベルは、パラメータと返り値型のなかから最も低いやつになる
  • 列挙型の個々のcaseは、列挙型のレベルと同じになる。個々のcaseに異なるレベルはつけられない
  • 列挙型のrawValueと 関連付けられた値は、列挙型と同じか高いアクセスレベルでなければならない
  • ネストされた型の暗黙のレベル
    • private型にネストされた型は、private
    • fileprivate型にネストされた型は、private
    • publicinternal型にネストされた型は、internal
  • サブクラスは、スーパークラスより高いレベルにできない
  • オーバーライドは、スーパークラスでのアクセスレベルより高くできる
  • 定数変数プロパティは、その値の型のレベルより高くできない
  • subscriptはインデックス型・返り値型より高くできない
  • プロパティ等のgetterとsetterの暗黙のアクセスレベルは、そのプロパティ等と同じになる
  • setterにgetterより低いアクセスレベルを与える public private(set) var x = 0
  • イニシャライザは 型の レベルより低くできる
  • required イニシャライザは型のレベルと同じでなければならない
  • デフォルトのイニシャライザの暗黙のレベルは、型のレベルと同じ(public以外)
    • public 型のデフォルトイニシャライザの暗黙のレベルは internal
  • 構造体のmemberwiseイニシャライザは、
    • メンバーのレベルのうち一番低いやつがprivateならprivate
    • メンバーのレベルのうち一番低いやつがfileprivateならfileprivate
    • それ以外は internal
  • プロトコルの各要素のアクセスレベルは、プロトコルのレベルと同じで、違えられない
  • プロトコル継承は、元のプロトコルのレベルまで持てる。高くはできない。
  • プロトコルのconformは、型のレベルよりも低く出来る
  • 型がプロトコルをconformするという文脈は、型のレベルとプロトコルのレベルの最小値
  • extension で定義したメンバーの暗黙のレベルは、元の型のものと同じ(最大internal)
  • extensionにデフォルトのレベルを指定 private extension {…}
  • extensionでプロトコルをconformする場合は extensionのレベルは指定できない。替わりにプロトコルのレベルがデフォルトのレベルになる
  • 元の型と同じソースファイルに書いたextensionから、元の型のprivateにアクセスできる

ふつうinternalだよね〜と言われたので、とりあえず全て無指定で生きて行こうと思うの。

Memory Safety

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

  • シングルスレッドでも競合は起きる
  • 次のすべての条件を満たす2つのアクセスがある場合、競合が発生する
    • 少なくとも1つは書き込み
    • メモリ内の同じ場所にアクセスする
    • 期間が重複する
  • メモリアクセス期間には「瞬時」か「長期」がある
  • ふつうは瞬間的。いくつかの場合「長期アクセス」がある。長期アクセスは他のアクセスとオーバーラップできる
  • inoutパラメータの書き込みアクセスは、他の非inoutパラメータの評価が全て終わった後、 関数呼び出しの期間続く
  • 競合するとランタイムエラー
  • ひとつの解決方法は、値をコピーすること
  • 2つの inout パラメータをとる関数に、同一の変数を渡すとコンパイルエラー
  • 構造体の mutatingメソッド は メソッド呼び出しの期間 self への 書き込みアクセスを持つ
  • x.mutatingMethod(&x)コンパイルエラー
  • 構造体・列挙型・タプルの値の一部を変更すると値全体が変更される場合に競合するおそれがある
  • 次の条件が適用される場合、コンパイラは、構造体プロパティへの重複アクセスが安全であることを証明できる

ちょっと意図しないところで出そうで怖いけど、 コンパイルエラーでもランタイムエラーでも「modification requires exclusive access」と言われるので、 言われたらまあ気づけるかな、と思う。

Automatic Reference Counting

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

  • ARCは「ちょうどうまくいく」
  • 参照カウンタが適用されるのはクラスのインスタンスだけ
  • 強い参照の循環ができるとメモリが開放されない
  • 強い参照の替わりに、弱い参照か、所有されていない参照を使えば良い
  • 弱い参照はオプショナル型の 変数 var でなくてはならない
  • weak var tenant: Person?
  • 所有しない参照は参照されるインスタンスの寿命が同じか長いときに使われる
  • 非オプショナル型として定義される
  • deallocateされたあと、参照先にアクセスするとランタイムエラー
  • unowned let customer: Customer
  • お互いに参照しあうとどっちかがOptionalになる←非Optionalにするとコンストラクタでselfにアクセスする2段階初期化問題があるから
  • 暗黙にunwrapされたOptionalな型にする方法がある
  • var capitalCity: City!
  • クロージャはクラスのインスタンスと同様に参照型
  • クロージャ本体がselfにアクセスするとselfをキャプチャーする
  • そのクロージャselfが指すオブジェクトのプロパティ値に入れると、循環参照ができてしまう
  • 「キャプチャリスト」をつくることで、この問題を解決する
  • クロージャの開始{と(引数リスト)との間に
  • 引数リストと返り値方が型推論される時は、クロージャの開始{inとの間に
  • [unowned self, weak delegate = self.delegate!]
  • weak でキャプチャしたやつは Optional型

やっぱり参照カウンタからは逃れられなかったよ

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 {〜}