iOS 개발 시 자주 사용되는 Delegate 패턴은 순환 참조에 의한 메모리릭에 주의해야 합니다.
(순환참조 : https://outofbedlam.github.io/swift/2016/01/31/Swift-ARC-Closure-weakself/)
순환참조에 의한 메모리릭은 두 객체가 서로 잡고있어 하나가 사라지려고 할 때 다른 한 쪽이 잡고 있으면 사라지지 않는 현상입니다. 먼저 메모리릭을 발생시키는 아래 코드를 살펴봅시다.
protocol LeakedModelDelegate {
}class LeakedModel {
var delegate: LeakedModelDelegate?
deinit {
print(“deinit LeakedModel”)
}
}class LeakedViewController: UIViewController, LeakedModelDelegate {
var model: LeakedModel?
override func viewDidLoad() {
super.viewDidLoad()
model = LeakedModel()
model?.delegate = self
}
deinit {
print(“deinit LeakedViewController”)
}
}
LeakedViewController의 viewDidLoad에서 LeakedModel을 생성하고, 생성된 LeakedModel의 delegate 프로퍼티에LeakedModelDelegate를 지정한 LeakedViewController의 인스턴스인 self를 지정했습니다. 생각없이 위와 같이 작성하고, LeakedViewController가 화면에서 사라질 때 LeakedModel로 사라질거라 생각할 수 있지만 delegate에 의해 만들어진 순환참조가 LeakedViewController도 LeakedModel도 해제되지 못하게 합니다. LeakedViewController가 화면에서 사라져도(네비게이션 컨트롤러에 의해 pop되도) LeakedModel과 LeakedViewController의 deinit이 호출되지 않습니다.
protocol ModelDelegate: class {
}class Model {
weak var delegate: ModelDelegate?
deinit {
print(“deinit Model”)
}
}class ViewController: UIViewController, ModelDelegate {
var model: Model?
override func viewDidLoad() {
super.viewDidLoad()
model = Model()
model?.delegate = self
}
deinit {
print(“deinit ViewController”)
}
}
델리게이트 패턴 사용 시 순환참조에 의해 발생되는 메모리릭을 막으려면, 델리게이트 선언 시 사용되는 protocol을 class 타입으로 제한하고, 델리게이트 프로퍼티를 선언하며 weak을 지정해야 합니다.
이제 실행 후 ViewController가 화면에서 사라질 때 deinit이 잘 호출되는지 확인해 봅시다. 포스팅에 사용된 코드는 아래 git에서 확인할 수 있습니다.
https://github.com/GoodMorningCody/ViewControllerDelegateMemoryLeakExample