[swift] 클로저와 인스턴스 간의 순환 참조

클로저와 인스턴스 간의 순환 참조는 메모리 누수의 원인이 될 수 있습니다. Swift에서는 순환 참조를 방지하기 위해 weak 혹은 unowned 키워드를 사용할 수 있습니다.

weak 참조

weak 키워드는 클로저와 인스턴스 간의 약한 참조를 만듭니다. 약한 참조는 클로저와 인스턴스 간의 강한 순환 참조를 방지하는 데 사용됩니다. 약한 참조는 참조하는 인스턴스가 메모리에서 해제될 경우 자동으로 nil로 설정됩니다.

class Person {
    var name: String
    lazy var greeting: () -> String = { [weak self] in
        guard let self = self else { return "" }
        return "Hello, \(self.name)!"
    }

    init(name: String) {
        self.name = name
    }
}

var person: Person? = Person(name: "John")
print(person?.greeting())
person = nil // person 인스턴스가 메모리에서 해제되어 약한 참조가 nil로 설정됩니다.

위의 예시에서 greeting 클로저는 [weak self]를 통해 Person 인스턴스를 약한 참조합니다. 이렇게 함으로써 Person 인스턴스가 메모리에서 해제되는 경우, 클로저 안에서 self를 사용할 때 nil을 반환하여 액세스 중지를 보장합니다.

unowned 참조

unowned 키워드는 클로저와 인스턴스 간의 약한 참조보다 더 강력한 참조를 만듭니다. unowned 참조는 참조하는 인스턴스가 메모리에서 해제되었을 때 올바로 동작하지 않을 수 있습니다. 따라서 unowned 참조는 참조하는 인스턴스가 항상 메모리에 유지되는 경우에만 사용해야 합니다.

class Car {
    var brand: String
    lazy var description: () -> String = { [unowned self] in
        return "I drive a \(self.brand) car."
    }

    init(brand: String) {
        self.brand = brand
    }
}

var car: Car? = Car(brand: "Tesla")
print(car?.description())
car = nil // unowned 참조인 경우에는 car 인스턴스가 메모리에서 해제될 때 문제가 발생할 수 있습니다.

위의 예시에서 description 클로저는 [unowned self]를 통해 Car 인스턴스를 참조합니다. 그러나 car 인스턴스가 메모리에서 해제되면 더 이상 유효한 참조가 없기 때문에, 클로저를 실행하려고 할 때 오류가 발생할 수 있습니다.

요약

클로저와 인스턴스 간의 순환 참조는 Swift에서 메모리 누수의 원인이 될 수 있습니다. 이를 방지하기 위해 weak 혹은 unowned 키워드를 사용하여 순환 참조를 해결할 수 있습니다. weak는 약한 참조를, unowned는 강한 참조를 만듭니다. 순환 참조를 방지하는 적절한 참조 유형을 선택하여 메모리 누수를 피할 수 있습니다.

참고 자료