詳解Swiftを通し読みしてみて学んだこと

January 03, 2015

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がすごく良い。プロトコルや標準関数で有名なところが一覧になってる。これ眺めてある程度理解できるとデバックでも役立ちそう(プロトコルに準拠していない系のデバッグメッセージ出てたけど、こういうことだったのかぁ的な)

参考にした本

詳解 Swift

記事中の参考コードは独自に内容を変えているものもありますが、本書からそのまま引用しているものもあります。


Profile picture

Written by morizotter who lives and works in Tokyo building useful things. You should follow them on Twitter