詳解Swift#6(Chapter9 メモリ管理)
前回に続き、詳解Swiftの写経を続ける。
- 作者: 荻原剛志
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2015/12/25
- メディア: 単行本
- この商品を含むブログを見る
chapter9 メモリ管理
Swiftでは
のみが参照型のデータ。多くの場合はメモリ管理を意識しなくてもよい(ホントか?)が、特定の状況下ではリファレンスカウンタはじめ、メモリ管理の概要を把握しておくべき、と書かれている。
参照型データとARC
- リファレンスカウンタの概念
class Person : CustomStringConvertible { let name:String var age:Int init(name:String, age:Int) { self.name = name; self.age = age } var description: String { return "\(name), \(age)" } deinit { print("\(name): deinit") } } var yuta:Person! = Person(name:"勇太", age:16) print(yuta) var x:Person! = yuta x.age++ print(yuta) print(yuta === x) yuta = nil // ここではリファレンスカウンタが 2 -> 1 になるだけなので、deinitされない x = nil //ここでリファレンスカウンタが0となるので、 deinitされる
演算子の話: ===
同一性を調べる、 !==
同一でないことを調べる。
- ARC(アーク)
- コンパイル時にリファレンスカウンタの増減タイミングを決定するメモリ管理方式
- Objective-C では手動で管理していた
強い参照の循環
- インスタンスが解放されない例
class Student : CustomStringConvertible { let name: String // 名前 var club: Club? // 所属クラブ init(name:String) { self.name = name } var description: String { var s = "\(name)" if let mem = club { s += ", \(mem.name)" } return s } deinit { // デイニシャライザ print("\(name): deinit") } } class Club { let name: String // クラブ名 var members = [Student]() // 所属学生のリスト init(name:String) { self.name = name } func add(p:Student) { // 学生を追加する members.append(p) p.club = self // 学生の所属も書き換える } deinit { // デイニシャライザ print("Club \(name): deinit") } } var tinyClub: Club! = Club(name:"昼寝同好会") var yuji:Student! = Student(name:"悠二") tinyClub.add(yuji) print(yuji) yuji = nil // deinitされない tinyClub = nil // deinitされない
インスタンスの循環参照が発生している場合はARCによってメモリ解放されない
- 弱参照 / 強参照
- 弱参照
weak
修飾子を変数の前に付ける- リファレンスカウンタの値に影響しない
- 強参照
- 通常の参照
- リファレンスカウンタの値に影響する
- 弱参照
参照の種類 | 定義の仕方 | リファレンスカウンタへの影響 | 備考 |
---|---|---|---|
弱参照 | weak 修飾子を付与 |
なし | オプショナル型である必要がある |
弱参照 | 通常の変数呼び出し | あり |
まとめるとこんな感じですね。
- 非所有参照
弱参照と同様にリファレンスカウンタに影響しない参照方法。 unowned
修飾子を付ける。型にオプショナルは指定できない。「変数の値がnilにならないことが明らかな場合」は弱参照ではなく、非所有参照を使う方が高速に動作し、Appleも推奨している。
var hg1: Hoge! = Hoge() unowned var hg2: Hoge = hg1 print(hg2) // Hoge hg1 = nil //print(hg2) <- error
参照先が解放された後で参照すると実行時エラーとなる。
オプショナルチェーン
オプショナル型はプロパティやメソッドの返り値で、nilが返る可能性がある場合に使われる。オプショナル型の値を !
で開示して使用する場合、その値がnilなのかをチェックしなければいけない。
などの記述も用意されているが、オプショナルチェーンもその1つ。
- オプショナルチェーン
class Student { let name: String // 名前 weak var club: Club? // 弱い参照 init(name:String) { self.name = name } } class Teacher { let name: String // 名前 var subject: String? = nil // 担当教科 init(name:String) { self.name = name } } class Club { let name: String // 名前 weak var teacher: Teacher? // 弱い参照 var budget = 0 // 予算 var members = [Student]() init(name:String) { self.name = name } func add(p:Student) { members.append(p) p.club = self } func legal() -> Bool { // 公認クラブか? return members.count >= 5 && teacher != nil } } var who: Student? = Student(name: "kentana20") //who!.club!.teacher!.name <- error if let w = who { if let cl = w.club { if let tc = cl.teacher { print(tc.name) } } } // ↑をオプショナルチェーンで書くと↓ if let name = who?.club?.teacher?.name { print(name) }
!
による開示の代わりに ?
を使って目的の値を記述する。どこかでnilが返る場合はオプショナルチェーンの値はnilとなる。「値がある場合とない場合で処理が異る場合」は重宝しそう。
- オプショナルチェーンでメソッド呼び出し
var recognized = who?.club?.legal()
この場合、recognizedはBool?型(オプショナル)となる。
所感・雑感
- 参照型のデータを扱う場合はリファレンスカウンタを意識しないといけない
- 弱参照(weak) / 非所有参照(unowned) をうまく使えば参照循環を作らず、メモリにやさしい処理になる
- オプショナルチェーンは宣言的に書けてよいが、値はオプショナル型であることに注意する