投稿の経緯
循環参照を避けるための手段として使うweak
を付与したインスタンスがnilになるケースに遭遇したので備忘録として投稿。
循環参照とは
循環参照とは「クラスAとクラスBのインスタンスがそれぞれ存在した時、クラスAのインスタンスをクラスBのプロパティに格納し、クラスBのインスタンスをクラスAのプロパティに格納する」といった状態になった時に、お互いにインスタンスを参照しあうため、どちらも解放されずにそのまま残り続けてしまう、という現象です。
下記記事から引用。
weak varがnilになったケースのコード
Controller
Controller.swift
final class Controller {
private let useCaseInput: InputPort
init(input: InputPort) {
self.useCaseInput = input
}
func onButtonTapped() {
useCaseInput.callPresenter()
}
}
UseCase
UseCase.swift
protocol InputPort {
func callPresenter()
}
protocol OutputPort: AnyObject {
func callOutput()
}
final class UseCase: InputPort {
private weak var output: OutputPort?
init(output: OutputPort) {
self.output = output
}
func callOutput() {
output?.showLog()
}
}
Presenter
Presenter.swift
final class Presenter: OutputPort {
func callOutput() {
print("hoge")
}
}
原因
UseCaseのinitが呼ばれ、self.output
に初期値が代入された後、self.output
は何も処理することがなく、役割を終え、弱参照(weak)に従ってインスタンスが削除されます。UseCaseのinitが呼ばれたのちにPresenterでdeinit
を書いておくと原因が明確になると思います。
対処法
今回のパターンは循環参照ではないのでweak
を付与せずに書けば解決します。
UseCase.swift
protocol InputPort {
func callPresenter()
}
protocol OutputPort: AnyObject {
func callOutput()
}
final class UseCase: InputPort {
private let output: OutputPort
init(output: OutputPort) {
self.output = output
}
func callOutput() {
output.showLog()
}
}
結論
当然といえば当然ですが、weak
は循環参照が存在する場合のみ付与しましょう!