weak self와 unowned self
Swift의 closure에서는 종종 weak self와 unowned self를 볼 수 있다.
이 둘은 순환 참조를 끊는 해결책으로 사용된다.
closure에서는 왜 순환 참조 문제가 발생하는가?
closure는 정의된 컨텍스트 안에서 모든 상수 및 변수 캡쳐할 수 있기 때문이다.
그럼 순환 참조 문제가 발생하는 아주 간단한 예제를 보자
class Test {
var number = Int()
lazy var test: () -> (Int) = {
self.number += 1
return self.number
}
init(number: Int) {
self.number = number
}
deinit {
print("Test is done.")
}
}
var test: Test? = Test(number: 10)
print(test!.test()) // 11
test = nil // 메모리에서 Test가 deallocate 되지 않는다.
weak self로 순환 참조 해결하기
간단히 Test 클래스의 closure 선언부에 [weak self]만 추가하면 해결된다.
class Test {
var number = Int()
lazy var test: () -> (Int) = { [weak self] in
if var number = self?.number {
number += 1
return number
} else {
return Int()
}
}
init(number: Int) {
self.number = number
}
deinit {
print("Test is done.")
}
}
var test: Test? = Test(number: 10)
print(test!.test()) // 11
test = nil // Test is done.
unowned self로 순환 참조 해결하기
마찬가지로 [unowned self]만 추가하면 해결된다.
class Test {
var number = Int()
lazy var test: () -> (Int) = { [unowned self] in
self.number += 1
return self.number
}
init(number: Int) {
self.number = number
}
deinit {
print("Test is done.")
}
}
var test: Test? = Test(number: 10)
print(test!.test()) // 11
test = nil // Test is done.
차이점
그렇다면 언제 weak self를 써야하고 언제 unowned self를 써야할까?
둘 다 순환 참조를 해결할 수 있는 방법이지만 unowned self는 옵셔널이 아니기 때문에 힙에 있지 않는다면 crash가 발생한다.
class Test {
var number = Int()
lazy var test: () -> (Int) = { [unowned self] in
self.number += 1
return self.number
}
init(number: Int) {
self.number = number
}
deinit {
print("Test is done.")
}
}
var test: Test? = Test(number: 10)
let a = test!.test
test = nil // Test is done.
a() // crash!!!