iOS 개발/Swift

[Swift] ARC (2/2)

꽁치대디 2022. 4. 29. 01:51

[Swift] ARC (2/2)

[Swift] ARC (1/2) 보러가기

 

[Swift] ARC (1/2)

[Swift] ARC (Automatic Reference Counting) 앱의 메모리 사용량을 추적하고 관리 어떠한 클래스 인스턴스가 더 이상 필요하지 않을 때 인스턴스에 할당된 메모리 자동해제 Reference Type 인 클래스 인스턴스

trumanfromkorea.tistory.com

클로저에 대한 강한 참조 사이클 (순환 참조)

순환 참조는 클래스 인스턴스의 프로퍼티에 클로저를 할당하고 해당 클로저의 바디에 인스턴스를 캡처하는 경우에도 발생할 수 있습니다. 클로저의 바디에서 self.someProperty 와 같이 인스턴스의 프로퍼티에 접근하거나 self.someMethod() 와 같이 인스턴스의 메소드를 호출하기 때문에 발생할 수 있습니다. 이러한 경우 클로저가 self 를 캡처하여 순환 참조를 발생시킵니다.

 

이런 문제는 클로저가 클래스와 같이 참조 타입 (reference types) 이기 때문에 발생합니다.

 

위의 HTMLElement 클래스는 태그 이름을 지정할 수 있는 name 프로퍼티와 태그 내에 렌더링 될 텍스트를 나타내는 text 프로퍼티를 가지고 있습니다.

 

이외에도 asHTML 이라는 지연 프로퍼티 (lazy property) 를 가지고 있습니다. 이 프로퍼티는 nametext 를 이용해 HTML 구문을 만드는 클로저를 참조합니다. asHTML 프로퍼티는 파라미터가 없고 String 값을 리턴하는 함수 타입입니다.

 

asHTML 프로퍼티는 옵셔널인 text 값의 존재 여부에 따라 리턴하는 값이 달라집니다. asHTML 프로퍼티는 인스턴스 메소드와 비슷하게 이름이 지어지고 사용되지만, 이는 클로저 프로퍼티이기 때문에 특정 HTML 태그에 대해 렌더링을 변경하고 싶다면 사용자 정의 클로저로 asHTML 프로퍼티의 기본값을 대체할 수 있습니다.

예를 들자면 asHTML 프로퍼티에 text 프로퍼티가 nil 일 경우 일부 텍스트를 기본값으로 하는 클로저를 할당할 수 있습니다.

다음은 HTMLElement 클래스의 인스턴스 생성과 해제에 대해 알아보겠습니다.

위의 HTMLElement 클래스는 이 인스턴스와 기본 asHTML 값으로 사용된 클로저간에 순환참조를 발생시킵니다.

인스턴스의 asHTML 프로퍼티는 클로저에 대해 강한 참조를 유지합니다. 그러나 클로저는 바디 내에서 self.nameself.textself 를 참조하기 때문에 클로저는 HTMLElement 인스턴스에 대해 강한 참조를 유지한다는 의미로 self 를 캡처합니다. 그렇기에 둘 사이에 순환 참조가 발생합니다.

 

그렇기 때문에 paragraph 변수에 nil 을 할당하여 HTMLElement 인스턴스에 대한 강한 참조를 끊더라도 순환 참조 문제 때문에 해당 인스턴스와 클로저는 해제되지 않습니다.

클로저에 대한 순환 참조 해결

클로저의 정의의 부분으로 캡처 목록을 정의하여 클로저와 클래스 인스턴스 사이의 순환 참조 문제를 해결합니다. 캡처 목록은 바디 내에서 하나 이상의 참조 타입을 캡처할 때 사용할 규칙을 정의합니다. 두 클래스 사이의 순환참조와 마찬가지로 캡처된 각 참조를 강한 참조가 아닌 약한 참조 혹은 미소유 참조로 선언합니다.

캡처 목록 정의

캡처 목록에서 각 항목은 클래스 인스턴스 (ex: self) 또는 어떤 값으로 초기화된 변수 (ex: delegate = self.delegate) 에 대한 참조와 함께 weak 혹은 unowned 참조와 쌍을 이룹니다.

 

캡처 목록은 클로저의 파라미터 앞에 위치합니다. 만약 클로저가 파라미터나 리턴값을 가지지 않는다면 그건 컨텍스트를 통해 추론이 가능하기 때문에 캡처 목록을 클로저의 시작 부분에 in 키워드를 이용하여 위치합니다.

Weak & Unowned 참조

클로저와 캡처한 인스턴스가 항상 서로를 참조하고 같은 시간에 할당 해제된다면 클로저의 캡처를 unowned 참조로 정의합니다.

 

하지만 캡처된 참조가 nil 이 될 경우에는 weak 참조로 캡처를 정의합니다. 이는 옵셔널이기 때문에 참조하는 인스턴스가 해제되면 자동으로 nil 이 됩니다.

 

위 경우에서 캡처 목록은 self 를 미소유 참조로 캡처하겠다는 의미에서 [unowned self] 라고 사용합니다.

이번에는 클로저에 의해 self 의 캡처는 미소유 참조이고 캡처한 HTMLElement 인스턴스를 강하게 유지하지 않습니다. 이제 paragraph 변수에 nil 을 할당한다면 인스턴스는 해제되고 deinit 구문이 실행되는 것을 확인할 수 있습니다.

참고 링크

Swift Docs - ARC
Swift Docs 번역본

'iOS 개발 > Swift' 카테고리의 다른 글

[Swift] 타입 캐스팅 (Type Casting)  (0) 2022.05.19
[Swift] 프로토콜 (Protocols)  (0) 2022.05.13
[Swift] 확장 (Extensions)  (0) 2022.05.13
[Swift] ARC (1/2)  (0) 2022.04.29
[Swift] 클로저 (Closures)  (0) 2022.04.22