weak self와 unowned self

  • swift의 closure에서는 종종 weak selfunowned 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!!!