Swiftで簡単なアプリを既にリリースしているし、仕事で使う機会も少しある。でも、Swiftをきっちり理解しているわけではなくて、Web上の断片的な知識で何とかこなしたり、スマートでないだろう方法で実装したりしていた。一通りまとまったドキュメントを読んでおきたいなぁと思ったところで、「詳解 Swift」が発売されたので、年末年始の移動時間などでざっと目を通してみた。その際のメモ。
最初に、通読した本について簡単に言っておく。Swiftに関しては他の本は読んでいないけれど、この本はかなり良い。例題がなく言語仕様の解説ばかりなので、初心者には向かないと思うけれど、Objective-Cでアプリを作ったことがある人ならば、Swiftの全体像を知る上で良いのではないか。解説も骨太でわかりやすい。ちなみに、この著者が書いた「詳解 Objective-C 2.0 第3版」もObjective-C本としては骨太で名著でした。
気になった所、ちゃんと覚えたい所のメモ
感想も込み。主観も入っているので、本の内容と完全に一致しているわけではありません。
inout引数 - p.35
Swiftにはポインタが無いため、引数で渡ってきた変数の値を変更するためにinout引数を利用する。
func mySwap(inout a:Int, inout b:Int) { let t = a a = b b = t }
利用する際は、変数の前に&をつけてinout引数に対応する実引数であることを表す。
構造体は「構造を持つ1個のデータ」とする - p.55
1個のデータとする見方では、構造体の値を変更する場合、構成要素を書き換えるのではなく、構造体自体を新しく作る。Swiftでは構造体は整数や実数と同じ値型であるため、1個のデータと考えてプログラミングをすることが標準的。
構造体(struct)はclassと機能的にとても似ているので扱いをどうしようか迷っていたが、1個のデータと考えることで頭のなかがスッキリした。
添字付け - p.65
subscript(仮引数) -> 型 { get { 添字で指定したプロパティの値を返す文 } set(仮引数) { プロパティの値を更新する文 } }
setは省略可能。subscriptを実装することで、配列のように使うことができるようになる。
nil併合演算子 - p.74
var val = str.toInt() ?? 0
nilだったら2番目の値を返す。これはつい忘れてしまうのでメモ。
範囲型と区間型 - p.84
- 半開区間型 HalfOpenInterval
A..<B A ≦ x < B - 閉区間型 ClosedInterval
A…B A ≦ x ≦ B - 範囲型 Range
A..<B または A…B
for-in文で範囲として指定できるのは範囲型、switch文のcaseで区間として指定できるのは区間型。区間型と範囲型が曖昧になっていたので注意する。
Stride型 - p.86
var st1 = stride(from: 8, through:20, by:4) // 20を含む var sty = stride(from:8, to:20, by: 4) // 20を含まない
for文で1ずつではなく飛び飛びに評価したいときに利用する。下記のように配列を初期化することもできる。
let arr1 = [Double](stride(from:20.0, through: 0.0, by: -0.5)
部分配列 - p.95
配列の添字に整数型の範囲を指定することによって、その添字の範囲の要素からなる新しい配列を作ることができる。
var array = [“A”, “B”, “C”, “D”, “E”] let sub = array[2…3] println(sub) // [“C”, “D”]
ここでできたsubは文字列の配列([String])ではなく、Slice
let subarray = [String](sub)
パターンマッチ演算子 - p.143
~= : 区間に含まれるかどうかをBoolで返す。
0..<8 ~= 8 // false 0…9.8 ~= 2.45 // true
短絡評価 - p.145
func あ(lhs:Bool, rhs:@autoclosure() -> Bool) -> Bool { if lhs { return false } return !rhs() }
lhsが否定だった場合に、rhsを評価しない。
Any型 - p.160
AnyObject型で扱えるのはクラスのインスタンスだけだが、整数などの全ての型をまとめて扱うことができるAny型がある。
var every:[Any] = [5, “駅前”, 2.34]
AnyObject型の実体はプロトコル。Any型の実体も多分プロトコル。
=== - p.185
複数箇所から参照されているインスタンスが同一のものかを調べるための演算子として「===」が用意されている。これは、インスタンスの値が等しいかどうか(同値性)ではなく、メモリ上にある同じインスタンスの実体を指しているか(同一性)を調べる。
弱い参照、非所有参照 - p.190, p.192
- 弱い参照(weak reference): 参照しているインスタンスが開放されることがある場合に利用する。
- 非所有参照(unowned reference): 参照しているインスタンスが開放されることがない場合に利用する。参照していたインスタンスが開放されている場合にアクセスすると落ちる。
関連して、Closureでself.variable などとselfの変数にアクセスした場合、キャプチャされるのはvariableではなくself。なので、Closureで[unowned self]や[weak self]という書き方をして循環参照を避ける。
オプショナルチェーンへの代入 - p.200
who?.club?.teacher = someTeacher
オプショナルチェーンに代入もできたのか。
付属型 - p.218
プロトコル内でtypealiasを使って宣言される型パラメータ(及びそれに寄って表される型)を付属型(associated type)と呼ぶ。付属型はジェネリクスの機能の1つで、型の定義にプロトコルが採用された時、その型で実際に使われる型にマッチし、プロトコルの記述を実際の定義に置き換えるはたらきがある。
プロトコルの付属型について規定の型を宣言しておく例
protocol Vector2D { typealias DataType var x: DataType { get set } var y: DataType { get set } } typealias DataType = Float
付属型が適合するプロトタイプを指定
protocol Quotable { typealias TargetType : Printable var quotation: TargetType { get set } func printQuotation() }
プロトコル定義におけるSelf - p.221
Selfはプロトコルが具体的な方の定義で使われるとき、定義されるその型自身で置き換えて記述すべきであることを示す。
関数のカリー化 - p.256
引数a,bを持ち、結果rを返す関数があった時、その関数をカリー化した関数とは、引数がaで「引数bを持ち、結果rを返す関数」を返すような関数。
func timesCurried(a: Double)(b: Double) -> Double { return a * b }
var inch = timesCurried(2.54)
inchは引数の実数に2.54の関を計算する関数として利用することができる。
println(inch(b: 20.0)) // 50.8を表示
C言語のポインタとSwiftの型 - p.281
- UnsafePointer
- UnsafeMutablePointer
- AutoreleasingUnsafeMutablePointer
UnsafeはARC管理下にないということ。C言語の型とSwiftの型の互換表はp.282を参照する。
Swiftでは軽量マークアップ言語処理系であるreStructuredTextを使ってソースコード内のコメントを処理する - p.323
その他
巻末のApprendixがすごく良い。プロトコルや標準関数で有名なところが一覧になってる。これ眺めてある程度理解できるとデバックでも役立ちそう(プロトコルに準拠していない系のデバッグメッセージ出てたけど、こういうことだったのかぁ的な)
参考にした本
記事中の参考コードは独自に内容を変えているものもありますが、本書からそのまま引用しているものもあります。