티스토리 뷰

반응형

 

 

 


Strong Reference Cycles for Closures
클로져에 대한 강한참조순환

 

앞선 포스팅에서는 두 개의 클래스 인스턴스 프로퍼티가 서로 강한참조를 갖게 되면 어떻게 강한순환참조가 발생하는지를 다뤄봤습니다. 또한 약한참조(weak reference)를 사용해서 강한순환참조를 깨는 방법과 미소유참조(unowned reference)를 사용해서 강한순환참조를 깨는 방법도 다루었었습니다.

클래스 인스턴스의 프로퍼티에 클로져를 할당할 때, 그리고 해당 클로져가 인스턴스를 캡쳐하는 경우 강한참조순환이 발생할 수 있습니다. 이때의 캡쳐에서 클로져의 몸통이 아래와 같은 인스턴스의 프로퍼티를 접근하기 때문에 발생할 수 있습니다.

self.someProperty

 

혹은 아래처럼 클로져가 인스턴스에 대한 메서드를 호출하는 경우도 해당됩니다.

self.someMethod()

 

 

두 가지 케이스 모드 이러한 접근들이 클로저가 self를 캡쳐하고 강한참조순환을 초래할 수도 있습니다. 이러한 강한순환참조 또한 참조타입인 클래스 형태의 클로져이기 때문에 발생할 수 있습니다. 당신이 프로퍼티로 클로져를 할당할 때, 클로저에 대한 참조를 할당하게 됩니다. 본질적으로 보면, 앞서 보았던 두개의 참조가 서로 붙잡고 유지되는 문제와 같습니다.

Swift는 클로져 캡쳐 리스트 (Closure Capture List) 라고 불이는 이 문제에 대한 우아한 해결책을 제공합니다. 그러나, 이러한 경우의 강한참조순환 해결방법을 배우기 전에, 왜 이러한 순환이 초래되는지를 이해하는 것이 필요합니다.

아래의 예시는 self를 참조하는 클로져를 사용할 때, 어떻게 강한참조순환이 생기는지를 보여줍니다. 아래의 예시는 HTMLElement라는 클래스를 정의합니다. HTMLElement는 HTML 문서 내의 개인적인 요소에 대한 간단한 모델을 제공합니다.


HTMLElement 클래스는 name 프로퍼티를 정의하는데, name은 "h1", "p", "br"과 같은 원소들의 이름을 가리킵니다. HTMLElement 또한 옵셔널 프로퍼티, text: String? 을 정의합니다. text는 HTML 원소에 대해 렌더링 되는 텍스트를 표현하게 될 것입니다.

두 개의 간단한 프로퍼티들 뿐만 아니라, HTMLElement 클래스는 lazy 프로퍼티인 asHTML을 갖고 있습니다. 해당 프로퍼티는 nametext를 HTML 문자열 조각으로 조합해주는 클로져를 참조합니다. asHTML 프로퍼티는 () -> String 타입의 클로져입니다. 즉, 매개변수를 받지 않고, 문자열을 반환하는 클로져 타입입니다.

기본적으로 asHTML 프로퍼티에는 HTML태그의 문자열 표현을 반환하는 클로져가 할당됩니다. 해당 태그는 값이 존재하거나 존재하지 않을 수 있는 옵셔널 값, text를 포함하고 있습니다.

 

짧은 문장을 예시로 하자면, 해당 클로져는 <p>some text</p> 와 같은 값을 반환할 수도, text가 존재하지 않는다면 <p />와 같은 값을 반환할 수 있으며 이는 전적으로 text 프로퍼티가 어떤 문자열값이 존재하는지, nil인지에 달려있습니다.

asHTML 프로퍼티는 인스턴스 메서드와 다소 유사하게 명명되고 사용됩니다. 그러나 asHTML은 인스턴스 메서드라기보다 클로져 프로퍼티 이기때문에 당신은 asHTML 프로퍼티의 디폴트 값을 커스텀 클로져로 대체할 수 있습니다. 만약 특정 요소에 대한 HTML 랜더링을 바꾸고자 한다면 말입니다.

예를들어, asHTML 프로퍼티는 text 프로퍼티 값이 nil일 경우 디폴트 텍스트로 설정되는 클로져를 설정할 수 있습니다. 텅 빈 HTML 태그를 반환하여 표현하는 것을 방지하기 위해서 말이죠.

참고사항 : asHTML 프로퍼티는 lazy 프로퍼티로서 선언되어 있습니다. 왜냐하면 asHTML 프로퍼티는 HTML 출력 타겟에 대한 문자열로서 렌더링 될 필요가 있을때만 필요하기 때문입니다. asHTML은 default 클로져 내의 self를 당신이 참고할 수 있음을 의미하는 lazy 프로퍼티인데 lazy 프로퍼티는 초기화가 완료되고 self가 존재하기 전가지는 접근되지 않을 것입니다. 

 

 


HTMLElement 클래스는 단일 생성자들 제공하는데 이는 매개변수, nametext를 받습니다. 클래스는 소멸자 또한 정의하며 소멸자에서는 HTMLElement가 해제 될 경우 이를 문구출력으로 알려줍니다.

아래 코드는 어떻게 HTMLElement 클래스가 만들어지고 어떻게 새로운 인스턴스를 출력하는 지를 보여줍니다.

참고 : 위의 변수, paragraph는 optional HTMLElement로서 정의되며 이는 강한순환참조의 존재를 보여주기 위해서 아래에서 nil로 셋팅됩니다.

 

 


불행하게도, 상기한 HTMLElement 클래스는 HTMLElement 인스턴스와 default asHTML 값으로 사용 되는 클로져 사이에 강한순환참조가 생성됩니다. 아래는 어떻게 강한순환참조가 발생하는지를 보여줍니다.

클로져가 self를 캡쳐하면서 클로져와 인스턴스 간 강한참조순환이 발생하였습니다. 


HTMLElement 인스턴스의 asHTML 프로퍼티는 클로져에 대한 강한 참조를 갖고 있습니다. 그러나, 클로져는 해당 인스턴스 몸체의 self를 참조하고 있기 때문에 (self.name, self.text를 참조하는 방법으로서) 이때 클로져는 self를 캡쳐합니다. (Closure Capturing). 이는 클로져가 HTMLElement 인스턴스에 대한 강함참조를 잡고 있음을 의미합니다. 이로서 HTMLElement 인스턴스와 클로저 같의 강한참조순환이 발생합니다.
* 클로져 내의 Capturing Values 에대한 더 많은 정보를 원하시면, Capturing Vaues를 참조하세요.

참고 : 비록 클로져가 self 를 여러번 참조하지만, HTMLElement 인스턴스에 대한 단 하나의 강한 참조만을 캡쳐합니다.

 



만약 paragraph 변수를 nil로 설정하여 HTMLElement 인스턴스에 대한 강한참조를 깨고 싶어도 HTMLElement 뿐만아니라 HTMLElement 내 의 클로져는 해제되지 않습니다. 강한참조순환 떄문이지요.

paragraph를 nli로 설정해도 강한참조순환이 발생하여 메모리 해제가 정상적으로 되지 않습니다. 


이로서 HTMLElement의 소멸자는 실행되지 않고, 해제에 대한 출력문구도 나타나지 않습니다.




 

그렇다면 클로져와 인스턴스 간의 클로져캡쳐링문제, 이런 경우의 강한참조순환문제는 어떻게 해결할 수 있을까요?
다음 포스팅에서 다뤄보겠습니다. 의견 환영합니다. 감사합니다 ^-^//



 

참고 문서 링크 ▼

Swift Automatic Reference Counting

 

 

반응형
댓글
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함