아래 세개의 버전 중 순환참조가 발생하는 녀석은 뭐가 있을까?
// Version A
class viewControllerA: UIViewController {
var workItem: DispatchWorkItme?
override func viewDidLoad() {
let workItem = DispatchWorkItem {
UIView.animate(withDuration: 1.0) {
self.view.backgroundColor = .red
}
}
self.workItem = workItem
}
}
// Version B
class viewControllerA: UIViewController {
var workItem: DispatchWorkItme?
override func viewDidLoad() {
let view = self.view
let workItem = DispatchWorkItem {
UIView.animate(withDuration: 1.0) { [weak self] in
view?.backgroundColor = .red
}
}
self.workItem = workItem
}
}
// Version C
class viewControllerA: UIViewController {
var workItem: DispatchWorkItme?
override func viewDidLoad() {
let workItem = DispatchWorkItem {
UIView.animate(withDuration: 1.0) { [weak self] in
self?.view.backgroundColor = .red
}
}
self.workItem = workItem
}
}
정답은 세 버전 모두 순환참조가 일어난다는 것이다.
무턱대고 weak self를 남발해도 제대로 알지 못하면 메모리릭을 일으킬 수 있다는 것이다.
개발을 하다보면 복잡한 클로저들을 만들 때가 있다
retain cycle을 피하기 위해 [weak self] 를 써주어야 하는데 이러한 상황에서 모든 클로저에 weak self를 다 써줘야 할까?
class ViewControllerB: UIViewController {
var workItem: DispatchWorkItem?
override func viewDidLoad() {
let view = self.view
let workItem = DispatchWorkItem {
UIView.animate(withDuration: 1.0) { [weak self] in // this leaks
view?.backgroundColor = .red
}
}
self.workItem = workItem
}
}
처음에 UIview.animate에 weak self를 집어넣어주고 view 프로퍼티에 접근을 위해 weak self를 넣었지만 이것은 순환참조를 일으키니 DispatchWirkItem에 넣어주어야 한다.
workItem은 self.workItem = workItem이라는 강한참조가 있는데 클로저는 self에 대한 강한참조도 저장한다.
이 self에 대한 강한 참조는 중첨된 클로저 내부에서 weak self로 표현된 버전 B에서도 발생된다,.
이 경우 weak self를 추가하는 것만으로도 실수로 자기에 대한 강한 참조가 생성되어 순환참조가 발생된다.
Version B에서 self에 대한 강한참조가 발생되는데 animtate 클로저 안에 [weak self]를 넣어주어서 그런 것이다.
이를 해결하기 위해서는 어떻게 해야 하는가?
DispatchQueue 부분에 weak self를 옮겨 주어야 한다.
만약 [weak self]가 낮은 레벨로( 복잡한 클로저 안 더 깊숙히) 이동하게 되면 메모리릭을 경험할 것이다.