LoginSignup
2
2

More than 1 year has passed since last update.

[Swift]weak var がnilになるケースに遭遇した!

Posted at

投稿の経緯

循環参照を避けるための手段として使う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は循環参照が存在する場合のみ付与しましょう!

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2