앞의 포스팅에서 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) 참조를 지정할 수 있다.
클래스 인스턴스 참조와 동일하게 생애주기를 참고하여 weak
와 unowend
를 결정하면 된다.
그렇다면 위의 예시 코드를 해결해보자.
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에 관련한 포스팅이 끝났다.
후... 힘들었다.ㅋㅋㅋ
그럼 이만👋