티스토리 뷰
Auto Reference Counting - Part 7
( a.k.a ARC )
Swift 5.0 - The Swift Programming Language (Apple Inc.) 번역
ARC 1편 - ARC 의 개요와 작동 원리 (https://atelier-chez-moi.tistory.com/37)
ARC 2편 - 강한 순환 참조 (https://atelier-chez-moi.tistory.com/40)
ARC 3편 - 강한 순환 참조 해결하기(1) : 약한 참조 (https://atelier-chez-moi.tistory.com/41)
ARC 4편 - 강한 순환 참조 해결하기(2) : 미소유 참조 (https://atelier-chez-moi.tistory.com/48)
ARC 5편 - 미소유 참조와 암시적 언래핑된 옵셔널 프로퍼티 (https://atelier-chez-moi.tistory.com/50)
ARC 6편 - 클로저에서의 강한 순환 참조 Part. 1 (https://atelier-chez-moi.tistory.com/51)
ARC 7편 - 클로저에서의 강한 순환 참조 Part. 2 & 클로저에서의 강한 순환 참조 해결하기 Part. 1 (현재 글)
Strong Reference Cycles for Closures
Part 1. 국문 요약
앞에서 두 클래스 인스턴스 프로퍼티가 서로를 강한 참조하고 있을 때, 어떻게 강한 순환 참조가 만들어질 수 있는지 보았어요. 또한 강한 순환 참조를 부수기 위해서 약한(weak) 참조와 미소유(unowned) 참조를 어떻게 사용하는지 또한 살펴보았죠.
또한 강한 순환 참조는 발생할 수 있는데, (그 이유는) 만약 우리가 클래스 인스턴스의 프로퍼티에 클로저(Closure)를 할당하고, 그리고 그 클로저의 바디가 인스턴스를 붙잡고 있는 경우가 있어요. 이러한 캡쳐(Capture, '획득')는 발생하게 될 수 있는데, (그 이유는) 클로저의 바디가 인스턴스의 프로퍼티(self.someProperty)에 접근하기 때문이거나, 클로저가 인스턴스의 메소드(self.someMethod())를 호출하기 때문이다. 어떠한 경우던, 이러한 접근은 클로저가 강한 순환 참조를 만들어내는 self 를 "캡쳐(획득)하도록(to "capture")" 만들어 낼 수 있다. (+ 이 부분 뭔가 깔끔하지 않네요..)
강한 순환 참조가 발생하는 이유는 클래스처럼 클로저는 참조 타입 (reference types) 이기 때문이죠. 우리가 클로저를 프로퍼티에 할당할 때면, 우리는 그 클로저에 참조 를 할당하고 있는 것이죠. 본질적으로, 이것은 앞에서 다뤄본 것과 같은 문제입니다. 두 개의 강한 참조가 서로가 살아있는 상태를 유지하는 것입니다. 그러나, 두 개의 클래스 인스턴스라기보다 이번에는 클래스 인스턴스와 클로저가 서로를 살아있도록 유지하고 있는 것이라 보시면 되겠습니다.
Swift 는 이 문제에 대해 엘레강스한 솔루션을 제공합니다. 클로저 캡쳐 리스트(closure capture list) 라 알려진 것이죠. 하지만 클로저 캡처 리스트로 강한 순환 참조를 부수는 방법을 학습하기 전에, 어떻게 그러한 순환이 유발될 수 있는 지를 이해하는 것은 유용할 것입니다.
아래의 예제는 클로저를 사용하여 self 를 참조할 때 우리가 어떻게 강한 순환 참조를 만들어 낼 수 있는지를 보여줍니다. 이 예시는 HTMLElement 라는 클래스를 정의하고 있으며, HTML 문서 내 개별 요소에 대한 간단한 모델을 제공하고 있어요 :
HTMLElement 클래스는 name 프로퍼티를 정의하고 있어요. 얘는 "h1", "p", "br" (+ HTML을 잘몰라서..) 과 같은 요소의 이름을 나타냅니다. HTMLElement 는 또한 옵셔널 타입의 text 프로퍼티를 정의하고 있는데, 해당 HTML 요소 내에서 렌더링 되는 텍스트를 나타내는 문자열로 설정할 수 있습니다.
이 두 개의 단순한 프로퍼티에 더하여, HTMLElement 클래스는 asHTML 이라는 lazy(+ 호출되는 순간에 초기화된다는 표시) 프로퍼티를 정의합니다. 이 프로퍼티는 name 과 text 를 HTML 문자열 조각으로 결합하는 클로저를 참조하고 있어요. asHTML 프로퍼티는 (타입이) () -> String 타입이거나 "파라미터를 가지지 않으며 문자열 값을 리턴하는 함수" 입니다.
기본적으로 asHTML 프로퍼티는 HTML 태그의 문자열 표현을 리턴하는 클로저로 할당됩니다. 이 태그는 텍스트가 있는 경우 옵셔널 타입의 텍스트 값을 포함하며, 혹은 텍스트가 없으면 텍스트의 컨텐츠는 없습니다. 단락 요소의 경우, 클로저는 "<p>some text</p>" 나 "<p />" 둘 중 하나를 반환하는데, 이는 text 프로퍼티가 "some text" 또는 nil 인지 여부에 따라 결정됩니다.
asHTML 프로퍼티는 이름이 지정되고 다소간 인스턴스 메소드처럼 사용됩니다. 하지만 asHTML 이 인스턴스 메소드라기 보다는 클로저 프로퍼티이기 때문에, 우리는 asHTML 프로퍼티의 디폴트 값을 커스텀 클로저로 대체할 수 있어요. 만약 우리가 특정 HTML 요소에 대해 HTML 렌더링을 변경하기를 원한다면요.
예를 들면 asHTML 프로퍼티는 만약 text 프로퍼티가 nil 이라면, 표현식(representation)이 빈 HTML 태그를 리턴하는 것을 예방하기 위해서 일부 텍스트를 기본값으로 가지는 클로저를 설정할 수 있습니다. (+ asHTML 프로퍼티 내에 if 로 정의한 부분을 이야기하고 있습니다.)
Part. 2 시작
NOTE
The asHTML property is declared as a lazy property, because it’s only needed if and when the element actually needs to be rendered as a string value for some HTML output target. The fact that asHTML is a lazy property means that you can refer to self within the default closure, because the lazy property will not be accessed until after initialization has been completed and self is known to exist.
asHTML 프로퍼티는 lazy 프로퍼티로 선언되는데, 요소(element)가 실제로 일부 HTML 출력 타겟에 대한 문쟈열 값으로서 렌더링 될 필요가 있을 때나 그런 경우에만 필요하기 때문에 lazy 프로퍼티로 선언됩니다. (+ 영문이 개인적으로 너무 난해한데, 저는 그냥 이 프로퍼티는 사용되는 시점에 초기화되는 프로퍼티이기 때문에 lazy 로 선언되었다는 맥락으로 이해했습니다) asHTML 이 lazy 로 선언된 프로퍼티라는 사실은 여러분이 기본 클로저 내에서 self 를 참조할 수 있다는 것을 의미하는데, 왜냐하면 lazy 프로퍼티는 초기화가 끝나고 self 가 존재할 때까지는 엑세스되지 않을 것이기 때문입니다.
The HTMLElement class provides a single initializer, which takes a name argument and (if desired) a text argument to initialize a new element. The class also defines a deinitializer, which prints a message to show when an HTMLElement instance is deallocated.
HTMLElement 클래스는 단일(single) 이니셜라이저를 제공하는데, 이 이니셜라이저는 name 이라는 argument 를 가지며, 새로운 요소를 초기화하기 위한 text 라는 argument 를 또한 가지고 있어요. 이 클래스는 또한 디이니셜라이저(deinitializer)를 정의하는데, 얘는 HTMLElement 인스턴스가 할당 해제(deallocated) 되었을 때를 보여주기 위한 메시지를 프린트해줍니다.
Here’s how you use the HTMLElement class to create and print a new instance:
다음은 여러분이 HTMLElement 클래스를 사용하여 새로운 인스턴스를 생성하고 프린트하는 방법입니다.
NOTE
The paragraph variable above is defined as an optional HTMLElement, so that it can be set to nil below to demonstrate the presence of a strong reference cycle.
위의 paragraph 변수는 옵셔널 타입 의 HTMLElement 로 정의되어 있으므로 강한 순환 참조의 존재를 증명하기 위해 맨 아래의 그림과 같이 nil 을 설정할 수 있습니다.
Unfortunately, the HTMLElement class, as written above, creates a strong reference cycle between an HTMLElement instance and the closure used for its default asHTML value. Here’s how the cycle looks:
불행하게도 HTMLElement 클래스는 위에 써진 것처럼 HTMLElement 인스턴스와 디폴트 asHTML 값에 사용되는 클로저 사이에 강한 순환 참조를 만들어내요. 다음은 순환이 어떻게 되어있는지 보여주고 있어요.
The instance’s asHTML property holds a strong reference to its closure. However, because the closure refers to self within its body (as a way to reference self.name and self.text), the closure captures self, which means that it holds a strong reference back to the HTMLElement instance. A strong reference cycle is created between the two. (For more information about capturing values in a closure, see Capturing Values.)
인스턴스의 asHTML 프로퍼티가 자신의 클로저에 대해 강한 참조를 가지고 있어요. 그러나 클로저는 자신의 바디 내에 self 를 참조하기 때문에 (self.name 과 self.text 를 참조하는 방식으로), 클로저는 self 를 캡쳐하는데, 이것은 클로저가 다시 HTMLElement 인스턴스에 대해 강한 참조를 가지는 것을 의미합니다. 강한 순환 참조는 이 클래스의 인스턴스와 프로퍼티 사이에서 생성됩니다. (클로져에서 값을 캡쳐하는 것에 대해 더 알고싶다면, 아래의 링크를 참고하세요.)
(https://docs.swift.org/swift-book/LanguageGuide/Closures.html#ID103)
NOTE
Even though the closure refers to self multiple times, it only captures one strong reference to the HTMLElement instance.
클로저가 self 를 여러번(multiple) 참조한다 할지라도, 클로저는 HTMLElement 인스턴스에 대해 하나의 강한 참조만을 캡쳐합니다.
If you set the paragraph variable to nil and break its strong reference to the HTMLElement instance, neither the HTMLElement instance nor its closure are deallocated, because of the strong reference cycle:
만약 여러분이 paragraph 변수에 nil 을 설정하고 HTMLElement 인스턴스에 대한 강한 참조를 부순다면, HTMLElement 인스턴스든 인스턴스의 클로저든 어느 것도 할당해제되지 않습니다. 강한 순환 참조 때문이죠:
Note that the message in the HTMLElement deinitializer is not printed, which shows that the HTMLElement instance is not deallocated.
HTMLElement 디이니셜라이져 내의 메시지는 출력되지 않는다는 것에 주목하세요.
이것은 HTMLElement 인스턴스가 할당해제되지 않았다는 것을 보여주는 것입니다.
Resolving Strong Reference Cycles for Closures
You resolve a strong reference cycle between a closure and a class instance by defining a capture list as part of the closure’s definition. A capture list defines the rules to use when capturing one or more reference types within the closure’s body. As with strong reference cycles between two class instances, you declare each captured reference to be a weak or unowned reference rather than a strong reference. The appropriate choice of weak or unowned depends on the relationships between the different parts of your code.
여러분은 클로저와 클래스 인스턴스 사이의 강한 순환 참조를 클로저의 정의의 일부분으로서 capture list 를 정의함으로써 해결할 것입니다. capture list 는 클로저의 바디 내 하나 이상의 참조 타입을 캡쳐링(획득)할 때 사용하기 위한 규칙을 정의해두었어요. 두 클래스 인스턴스간의 강한 순환 참조와 마찬가지로, 여러분은 강한 참조보다는 weak(약한) 혹은 unowned(미소유) 참조로 각각 캡쳐(획득) 된 참조를 선언합니다. 약한 혹은 미소유 참조의 적절한 선택은 여러분의 코드의 다른 부분들 간의 관계에 따라 달라지구요.
NOTE
Swift requires you to write self.someProperty or self.someMethod() (rather than just someProperty or someMethod()) whenever you refer to a member of self within a closure. This helps you remember that it’s possible to capture self by accident.
Swift 는 여러분에게 클로저 내의 self 의 멤버를 참조할 때마다 (단지 someProperty 혹은 someMethod() 보다) self.someProperty 혹은 self.someMethod() 를 작성할 것을 요구합니다. 이것은 여러분에게 우연에 의해 self 를 캡쳐(획득)하는 것이 가능하다는 사실을 기억하는 것에 도움이 됩니다.
다음 시간에는 ARC 마지막 시간을 가져보도록 하겠습니다!!!!
'Programming > Swift' 카테고리의 다른 글
Swift) String Slicing ? subString ?을 해보자. (2) | 2019.10.11 |
---|---|
Swift) 제네릭(Generics) (0) | 2019.08.28 |
1. Swift : ARC(Auto Reference Counting) (6) (0) | 2019.05.04 |
1. Swift : ARC(Auto Reference Counting) (5) (0) | 2019.05.02 |
1. Swift : ARC(Auto Reference Counting) (4) (0) | 2019.05.01 |
- Total
- Today
- Yesterday
- 컨버전
- 패캠
- Dictionary
- 리터럴
- inswag
- 깃허브
- 딕셔너리
- GCD
- lifecycle
- fallthrough
- 튜플
- 열거형
- OOP
- Swift
- swiftUI
- array
- 패스트캠퍼스
- iOS개발스쿨
- Operator
- 프로그래밍
- ios
- 스위프트
- 개발스쿨
- function
- 타입
- fastcampus
- var
- ARC
- commit
- tca
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 29 | 30 |