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 {…}
- プロコトルへのextension中のsubscriptにwhere句
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 {〜}
Type Casting
昨日に引き続き https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html を読む。
- Swiftの型キャストは
is
とas
演算子 - protocolをconformしているかどうかにも使える
- 型検査演算子
is
item is Movie
- 「subclassかどうか」だけど構造体・列挙型でも使える
- ダウンキャスト演算子
as?
とas!
- ダウンキャストは失敗するかもしれないので2つある
- ダウンキャストが成功するのがnot sureなときは
as?
を使え - ダウンキャストが成功するのがsureなときは
as!
を使え - “if let movie = item as? Movie” は、「
item
をMovie
としてアクセスしてみて、うまくいったらmovie
一時変数にオプショナル格納されてる値を入れてよね」と読む Any
には全ての型のインスタンスが入る。タプルや関数も入るAnyObject
に入るのはクラスのインスタンスだけswitch
〜case
で型検査する例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つある
- エラーを伝播させる
-
do
〜catch
構文でハンドリング - オプショナル値として
- エラーは起きないものとして
- 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文では、ステートメント外へ制御を移す
berak
やreturn
やthrow
はできない - 同一スコープで複数
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()
「オプショナル値の後ろに?
」 であって「?.
演算子ではない」、という解釈