unowned self

Swift의 클래스가 다른 인스턴스를 프로퍼티로 갖게 되는 경우, 만약 두 인스턴스가 서로를 프로퍼티로 참조하게 되면 상호간에 강한 참조 순환1이 발생하게 된다.

이를 해결하기 위해서는 변수 선언시에 weak이나 unowned를 명시하여 약한 참조가 발생하도록 한다.

weak

weak은 변수가 가리키는 대상이 더 이상 존재하지 않을 때 자동으로 nil로 인식되는 저장타입의 변수이다. 따라서 외부로부터 해당 값이 파괴될 때를 상정하여 옵셔널 타입으로 지정하여야 한다. 또한 이는 대상에 대해 참조 수를 올리지 않는다.

unowned

unownedweak와 유사하게 대상에 대한 참조수를 올리지는 않지만, 해당 객체가 항상 존재한다고 가정한다. 즉, nil 일 수 없으며 nil 값을 참조하게 되면 런타임 오류가 발생하게 된다.

예제

Swift 공식문서에 나오는 예제이다. 신용카드와 고객을 모델링했을 때, 신용카드의 경우에는 소유주가 항상 있지만, 고객의 경우에는 신용카드가 없을 수도 있다. 따라서 신용카드의 소유주는 unowned 형식이고 고객의 신용카드는 weak 형식이 되어야 한다.

class Customer {
    let name: String
    var card: CreditCard?
    init(name: String){
        self.name = name
    }
    deinit {
        println("(name) is being deinitializied.")
    }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number:UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit {
        println("Card #(number) is being deinitialized.")
    }
}

참고로 만약 A->B, B->A 모두 약한 참조라면 어떻게 될까? Customer.creditCardweak 옵션을 주면 다음과 같이 처리된다.

var john = Customer(name: "John Appleseed")
john.creditCard = CreditCard(number:1234_1234_1234_1234, customer:john)
//--> Card 1234123412341234 is being deinitialized.

크레딧 카드 인스턴스가 생성되었지만 john으로부터의 참조가 약한 참조이기 때문에 생성 즉시 바로 파괴되어 버린다.

클로저에서의 상호참조

Objective-C의 블럭에서와 마찬가지로 클로져는 주변 변수들을 캡쳐링하여 쓰게 된다. 이 때 클로저는 해당 변수들에 대해 강한 참조를 갖게 되는데 클래스 내에서 인스턴스 자신을 참조하는 클로저를 작성하면 역시나 강한 참조 순환으로 인해 메모리 누수가 발생할 수 있다. 이 때는 클로저의 타입 선언 부 맨 앞에 [unowned self]를 명시하는 별난 문법을 사용하여 이를 명시해주어야 한다.

class Customer {
    let name: String
    let work: ()->()
    init (name: String) {
        self.name = name
        self.work = { [unowned self] in 
            //do something...
        }
    }
}

  1. 두 인스턴스 A, B가 있을 때 A->B, B->A로 각각 강한 참조를 만들게 되면 A가 릴리즈되더라도 B에 의해 강한 참조가 유지되기 때문에 메모리 상에 객체값이 유지되며, B가 릴리즈되더라도 B 역시 A에 의해 강한 참조로 붙들려 있으므로, 결국 두 객체는 외부로부터의 참조점은 모두 잃었지만, 상호간의 참조 때문에 메모리상에 동떨어진 상태로 유지되고, 이는 메모리 누수로 이어진다. 보통 상호 참조가 필요한 경우 한쪽에서는 약한 참조를 써야 한다.