Swiftのメモリ管理
Swiftはメモリ管理する際はARC(Automatic Reference Counting)という方法になっている.
ARCとは
オブジェクトが他のオブジェクトから参照されている数を参照カウントとしてゲットしてきて、この参照カウントを追跡してインスタンスを解放をするかどうか判断する。
この参照カウントが一つでもあるとオブジェクトが破棄されることはなくインスタンスは解放されない。
0になった際にオブジェクトは破棄されてガーベジコレクタにいく。
Swiftではオブジェクトが参照した際はデフォルトで強参照になっている。
解放されたかどうかを確かめるには
deinitを使用する。解放されたprintされる。
書き方
class Fish {
var name: String
init(name: String) {
self.name = name
}
// デイニシャライザを定義する
deinit {
print("deinit!")
}
}
強参照の循環、循環参照
参照型同士のオブジェクトが参照しあっていると循環参照になり、参照カウントが絶対に1にならないようになってしまう。
class Fish {
var animal: Animal?
init() {}
deinit {
print("Fishクラスのインスタンスが解放されました")
}
}
class Animal {
var fish: Fish?
init() {}
deinit {
print("Animalクラスのインスタンスが解放されました")
}
}
AnimalクラスとFishクラスが参照しあっているため、どんなに少なくても参照カウントが1となり、メモリが解放されることはなくなってしまう。
また循環参照は別名、強参照の循環とも言われる。
循環参照の解決
手段は2つ
1weak(storyboardでよくみるあれ)
2unowned
class Fish {
// 弱参照させる
weak var animal: Animal?
init() {}
deinit {
print("Fishクラスのインスタンスが解放されました")
}
}
var、letの前にweak
をつけることで弱参照にし、参照カウント煮含めないようにする。参照しあっているオブジェクトが他にいなければカウントがゼロになり解放される。
weakに対してunowned
はクラスがオプショナル型ではなくnilであることを許容しない場合に使う。
class Fish {
var animal: Animal?
init() {}
deinit {
print("Fishクラスのインスタンスが解放されました")
}
}
class Animal {
unowned var fish: Fish//nilを許容しない
init() {}
deinit {
print("Animalクラスのインスタンスが解放されました")
}
}
クロージャー内の循環参照
クロージャーもインスタンスなのでクロージャー内でインスタンスを利用する場合は循環参照になる
class Animal {
let name: String
init(name: String) {
self.name = name
}
lazy var getInfo: () -> String = { [weak self] in
return "\(self.name)" // selfの参照カウントが+1になり解放されない
}
}
Animal→getInfo,getInfo→Animal(self)で循環参照してしまっていたため[weak self]
をつける(キャプチャリストという)ことで弱参照にできる。