ARC - 2

이원희·2021년 2월 20일
0

 🐧 Swift

목록 보기
22/32
post-thumbnail

앞의 포스팅에서 ARC와 참조 타입에 대해 알아봤다.
이번 포스팅에서는 클로저에서의 강한 참조 순환부터 알아보자.

클로저에서의 강한 참조 순환

강한 참조 순환은 변수 뿐만 아니라 클로저에서도 발생할 수 있다.
클로저에서 self를 캡쳐하기 때문에 발생할 수 있다.
그렇다면 클로저에서는 어떻게 강한 참조 순환을 피할 수 있을까?
코드를 먼저 살펴보자.

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")
    }
}

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())

paragraph = nil

// 출력
<p>hello, world</p>

asHTML 클로저는 지연 프로퍼티로 선언되어 있다.
HTML을 렌더링 하기 위해 필용한 태그와 텍스트가 준비되어야 사용할 수 있기 때문이다.
또한, 지연 프로퍼티이기 때문에 프로퍼티 안에서 self를 사용할 수 있다.

참조 관계를 살펴보자.

HTMLElement 클래스 인스턴스와 () -> String 클로저가 서로 strong 참조하고 있는 것을 볼 수 있다.
따라서 paragraph를 nil로 선언해도 deinit 구문이 출력되지 않는다.

캡쳐리스트 정의

캡쳐리스트를 정의하기 위해서는 클로저의 파라미터 앞에 소괄호를 넣고 그 안에 각 캡쳐 대상에 대한 참조 타입을 적어준다.

lazy var someClosure: (Int, String) -> String = {
    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
}

클로저의 파라미터가 없고 반환 값이 추록에 의해 생략 가능한 경우에는 캡쳐리스트 정의를 in 앞에 적어준다.

lazy var someClosure: () -> String = {
    [unowned self, weak delegate = self.delegate!] in
}

해결하기

그렇다면 어떻게 해결할 수 있을까?
캡처 참조에 강한 참조 대신 약한(weak) 참조 혹은 미소유(unowend) 참조를 지정할 수 있다.
클래스 인스턴스 참조와 동일하게 생애주기를 참고하여 weakunowend를 결정하면 된다.

그렇다면 위의 예시 코드를 해결해보자.

class HTMLElement {
    let name: String
    let text: String?
    lazy var asHTML: () -> String = {
        [unowned self] in
        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")
    }
}

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())

paragraph = nil

// 출력
<p>hello, world</p>
p is being deinitialized

unowned 참조로 캡쳐 리스트를 작성했다.
deinit 구문이 잘 찍히는 것을 확인할 수 있다.


마무리

드디어 ARC에 관련한 포스팅이 끝났다.
후... 힘들었다.ㅋㅋㅋ
그럼 이만👋

0개의 댓글