iOS/Common

[Swift] weak와 unowned의 차이

TDCIAN 2022. 3. 6. 23:29

요즘 RxSwift 습득에 열을 올리는 중입니다!

RxSwift로 구성된 코드를 보다 보면 weak self나 unowned self 코드를 보게 되는 경우가 많았습니다.

 

막연하게 강한 참조로 인해 발생하는 메모리 누수 문제를 해결하기 위한 방법으로 알고 있었는데,

둘 사이의 차이점이 무엇인지 궁금하여 찾아보다가 다음과 같은 포스팅을 발견하게 되어

공부도 할 겸 공유해 봅니다!

 

* 충분치 못한 영어 실력과 Swift에 대한 배경지식으로 번역한 것이기 때문에 다소간의 오류가 있을 수 있음을 감안해 주시면 감사하겠습니다!

 

영어를 충분히 잘하시는 분이시라면 원문으로 보시는 걸 추천드립니다!

 

원문 포스팅 주소입니다(영문).

https://cocoacasts.com/what-is-the-difference-between-weak-and-unowned-references-in-swift

 

What Is the Difference Between Weak and Unowned References in Swift

Automatic Reference Counting, or ARC for short, was introduced in Objective-C several years ago. It greatly simplifies memory management in Swift and Objective-C. Automatic Reference Counting usually works without you having to do anything. But there are s

cocoacasts.com

 

Swift에서 weak 참조와 unowned 참조의 차이는 무엇인가?

 

ARC(Automatic Reference Counting)은 몇 년 전에 Objective-C에 도입되었습니다. ARC는 Swift와 Objective-C에서의 메모리 관리를 매우 단순하게 만들어 줍니다. 보통의 경우 ARC는 여러분이 별도의 작업을 하지 않아도 알아서 작동합니다. 하지만 ARC에 도움이 필요한 몇 가지 경우가 있습니다.

강한 참조를 피하기 위해서, 여러분은 종종 컴파일러를 도와줄 필요가 있습니다. 이런 상황은 안타깝게도 많은 개발자들, 그중에서도 Objective-C에 대한 경험이 없는 개발자들을 괴롭게 만듭니다. weak 참조와 unowned 참조를 구분하여 사용하는 것은 종종 혼란을 일으킵니다. 이 글은 해당 부분에 초점을 두고 있습니다.

 

weak 참조와 unowned 참조란 무엇인가?

 

기본적으로 Swift에서 인스턴스에 대한 참조는 강한 참조입니다. 하지만 이게 항상 여러분들이 원하는 상황은 아닙니다. 만약 강한 참조가  적용되는 것이 적절하지 않은 상황이라면, 여러분은 weak 참조나 unowned 참조를 사용할 수 있습니다.

 

weak 참조

이름에서 알 수 있듯이, weak 참조는 인스턴스에 대하여 약한 참조 상태를 유지합니다. 즉 해당 인스턴스에 대한 참조가 ARC에 카운팅 되지 않는다는 것입니다. 만약 다른 객체가 특정 인스턴스에 대하여 강한 참조를 가지지 않는다면, 해당 인스턴스는 할당 해제가 된다는 것을 기억해 주세요.

 

unowned 참조

unowned 참조는 참조 대상에 대하여 강한 참조를 유지하지 않는다는 점에서 weak 참조와 비슷합니다. unowned 참조는 weak 참조와 동일한 목적으로 사용됩니다. 즉 강한 참조 사이클을 회피하기 위해 사용됩니다. 

하지만 weak 참조와 unowned 참조 사이에는 반드시 알아야 할 차이점들이 있습니다.

 

weak 참조와 unowned 참조의 차이점은 무엇일까

1. unowned 참조의 경우 항상 값이 존재할 것이라 가정한다.

우선적으로 알아야 할 차이는 unowned 참조의 경우 언제나 값이 존재할 것이라고 가정한다는 점입니다.

weak 참조의 경우에는 특정 인스턴스의 참조가 할당 해제된 경우에는 참조를 nil로 설정하게 됩니다.

 

2. weak 참조의 경우 항상 optional로 선언되기 때문에, 해당 값에 접근하기 전에 언래핑(unwrapping) 과정이 필요하다.

weak 참조의 경우 nil로 설정될 수 있기 때문에, 항상 optional로 선언됩니다.

이것이 weak 참조와 unowned 참조의 두 번째 차이점입니다.

weak 참조의 값은 접근하기 위해 언래핑해야 하지만, unowned 참조의 경우에는 언래핑 없이 바로 접근이 가능합니다.

하지만 unowned 참조가 적용된 인스턴스에서 해당 unowned 참조가 할당 해제된 경우에도 해당 인스턴스는 nil로 설정되지 않기 때문에, 만약 해당 객체에 접근하려 할 경우 fatal error가 발생합니다.

 

몇 가지 예시

weak 참조와 unowned 참조의 예시를 통해 이해를 해봅시다.

weak 참조의 예시부터 시작합니다.

 

Delegation

여러분은 delegation pattern에 익숙할 수도 있습니다. 여러분들은 아마도 UITableView의 class가 delegate property를 정의한다는 것을 알고 계실 겁니다. 다른 경우에도, delegate는 사용자와의 인터랙션을 담당하는 역할을 합니다.

예를 들어, 테이블뷰의 특정 셀이 선택되었을 때 뷰는 delegate에게 사용자의 인터랙션을 전달합니다.

테이블뷰는 delegate에 대해서 강한 참조를 유지하지 않습니다. 왜 그럴까요? 만약 테이블뷰가 delegate에 대하여 강한 참조를 유지할 경우, delegate는 테이블뷰에 대한 참조가 할당 해제된 경우에만 할당 해제될 것입니다. 그건 여러분들이 원하는 바가 아닐 겁니다.

뷰컨트롤러가 테이블뷰를 가지고 있고, 해당 뷰컨트롤러가 테이블뷰의 delegate로서 작동한다고 가정해 봅시다. 자주 사용되는 경우입니다. 이때, UITableView의 클래스에서 delegate가 강한 참조로 선언되어 있다면 뷰컨트롤러는 절대 할당 해제가 되지 않을 것입니다.

이 문제에 대한 이해를 돕기 위해 이 다이어그램을 봅시다.

 

만약 못 믿겠다면, UITableView 클래스의 public interface를 한 번 봅시다.

delegate와 dataSource는 weak property로 선언되어 있습니다.

 

property가 weak로 선언되어 있기 때문에, 강한 참조 사이클 문제는 발생하지 않습니다.

 

Accounts and Pricing Plans

이 예시는 unowned 참조를 설명할 때 자주 사용됩니다. 여러분이 Netflix에 가입한다고 가정했을 때, 여러분은 요금제를 선택하게 될 것입니다. 여러분은 여러분들이 원하는 대로 계정을 만들고 요금제를 선택할 것입니다. 이 상황을 코드르 표현한다면 다음과 같습니다.

더 나은 이해를 위해 아래 다이어그램을 한 번 보시겠습니다.

 

이 예시에서, 우리는 강한 참조 사이클을 생성했습니다. 계정 인스턴스는 요금제에 대하여 강한 참조를 유지하고 있고, 계정에 대한 요금제 역시 강한 참조를 유지한 상태로 연결되어 있습니다. property는 기본적으로 강한 참조의 속성을 가진다는 것을 기억합시다.

 

우리는 계정 property와 요금제 propery를 unowned로 선언함으로써 강한 참조 순환 문제를 해결할 수 있습니다.

 

다이어그램으로 보시면 이해가 더 빠르실겁니다.

계정 property에 weak 대신 unowned를 사용했는지 이해하는 것이 중요합니다. 

하나의 계정은 반드시 하나의 요금제를 가지며, 요금제는 계정 없이는 존재할 수 없습니다.

앞서 했던 말을 떠올려 봅시다. unowned 참조는 항상 값이 있을 것이라고 가정한다고 했습니다.

계정 인스턴스가 할당 해제 되었을 때, 계정 인스턴스에 연결되어 있던 요금제 인스턴스 역시 할당 해제 됩니다.

계정 propery가 기본 설정인 strong이 아니라 unowned로 선언되었기 때문에 가능한 상황입니다.

 

메모리 관리하기

메모리 관리에 대한 기본적인 내용과 강한 참조 순환의 회피에 대해 이해하는 것은 매우 중요합니다.

컴파일러가 여러분들의 메모리 관리에 대한 고민을 상당 부분 덜어주는 것은 맞지만, 여러분들은 참조 타입을 설정함에 있어 항상 주의를 기울일 필요가 있습니다.

 

 

* 혹시라도 잘못 번역된 내용이 있다면 댓글로 알려주시면 감사하겠습니다!

 

참고 내용

https://cocoacasts.com/what-is-the-difference-between-weak-and-unowned-references-in-swift