swift 循環参照の一例を挙げます。
iOSのソースを例にとります。
ViewController.swift
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let model = Model() model.action() } }
Model.swift
import Foundation class Model { private let message:String = "hello" private var myclosure:((Void)->Void)? init() { print("Model init") myclosure = { print("\(self.message)") } } deinit { print("Model deinit") } func action(){ myclosure?() } }
ここでmodelはローカル変数ですから、viewDidLoadを抜けた時は破棄されるはずです。 しかし結果は以下のようになり、"Model deinit"が表示されませんからdeinitが呼ばれず、破棄されていないことが分かります。
Model init hello
原因は、myclosureがselfを強参照した結果、modelがmyclosureを、myclosureがmodelを互いに参照してるためです。 ガベージコレクション方式でない、リファレンスカウンタ方式のメモリ管理方式を採用している言語ではこのようなことが起こります。 Modelのinit()を以下のように修正すると、破棄されるようになります。
init() { print("Model init") myclosure = { [unowned self] in // <- 修正 print("\(self.message)") } }
結果
Model init hello Model deinit
このケースでは、actionメソッドを実行中にmodelのインスタンスがなくなってしまうことはありませんので、
[unowned self]を使えばよいです。
ただし、actionメソッドを実行中にmodelのインスタンスがnilになってしまうことがあるような複雑なケースの場合は、[weak self]を使います。この場合、少しタイピング量が増えて、以下のような書き方になります。
init() { print("Model init") myclosure = { [weak self] in if let weakself = self { print("\(weakself.message)") } } }
selfがnilになってしまっても、オプショナルで判定して処理をスキップすることができます。