개발하는 동글 :]

[TIL],[Swift],[Closure 강한 참조 순환] 본문

카테고리 없음

[TIL],[Swift],[Closure 강한 참조 순환]

동글하다 2023. 8. 28. 11:32

1. 클로저의 강한 참조 순환은 왜 발생할까?

class HTMLElement {


    let name: String
    let text: String?


    lazy var asHTML: () -> String = {
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }


    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }


    deinit {
        print("\(name) is being deinitialized")
    }


}

해당 클래스는 nametext, lazy asHTML 속성을 가지고 있다.

그리고 asHTML속성을 살펴보면 text에 따라 값을 반환하는 함수이다.

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")

paragraph = nil
//print("\(name) is being deinitialized")

여기서 먼저 이러한 코드를 통해 paragraph를 nil로 할당해 주면

정상적으로 메모리에서 해제된 것을 확인할 수 있다.

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// Prints "<p>hello, world</p>"
paragraph = nil

하지만 lazy 속성을 사용한 후 nil을 할당하면 메모리에서 해제되지 않는 것을 확인할 수 있다.

왜 이런 일이 일어나는 걸까?

이미지에 나와있듯이 asHTML 내부의 textname이 해당 인스턴스를 강하게 참조하고 해당 인스턴스 또 한 함수를 강하게 참조하기 때문이다.

2. 클로저의 강한 참조 순환 해결

    lazy var asHTML: () -> String = { [weak self] in
        if let text = self?.text {
            return "<\(self?.name)>\(text)</\(self?.name)>"
        } else {
            return "<\(self?.name) />"
        }
    }

캡처될 항목을 사전에 weak나 unowned 키워드를 붙여서 정의해 준다면 실수로 캡처를 할 수 있는 상황을 방지할 수 있다.

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
//<Optional("p")>hello, world</Optional("p")>
paragraph = nil
//p is being deinitialized

정상적으로 메모리에서 해제됨

Reference

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting#Strong-Reference-Cycles-Between-Class-Instances