본문 바로가기
iOS/Swift

[Swift] ARC 개념 및 weak, unowned 알아보기 (2)

by 뚱이 2022. 1. 15.

이전 포스팅에서는 참조 count 가 0인 지점에 도달하는 간단하고 베이식 한 예시를 보여드렸다면 이번 포스팅에서는 참조 count 가 0인 지점에 도달하지 못하는 코드를 예시로 발생할 수 있는 문제점과 이를 해결하기 위한 방법은 무엇이 있는지 알아보겠습니다.

 

이전 포스팅 참조 

https://open95.tistory.com/entry/Swift-ARC-%EA%B0%9C%EB%85%90-%EB%B0%8F-weak-unowned-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-1

 

Swift 공식문서에 있는 위 예제 코드를 보시면 Person 클래스의 apartment 변수는 Apartment 클래스의 인스턴스를 소유하고 있고

Apartment 클래스에서 tenant 변수는 Person 클래스의 인스턴스를 소유하고 있습니다.

 

위와 같이 변수를 선언하고 인스턴스를 생성하면

변수 john, unit4A 는 각각 Person, Apartment에 강한 참조를 하고 있습니다. 또한, Person 인스턴스의  apartment와 Apartment의 tenant는 Optional 타입이기 때문에 nil로 초기화된 상태입니다. 이 상태에서 john의 apartment 변수에 unit4A를 unit4A의 tenant 변수에 john을 할당해 보겠습니다.

그 결과  인스턴스 안의 apartment, tenant 가 각각 Person, Apartment와 강한 참조가 발생하게 됩니다. 즉, Person과 Apartment의 참조 count는 2가 됩니다.

이 시점에서 john, unit4A 변수에 nil을 할당하여 참조를 해지해 보면 

다음과 같이 Person 인스턴스를 참조했던 john 변수는 강한 참조가 끊어지게 되고 unit4A도 마찬가지로 Apartment 인스턴스와의 강한 참조가 끊어지게 되지만 apartment, tenant 변수가 각각 상호 참조를 하고 있어 참조 count가 1이기 때문에 이 두 인스턴스는 해지되지 않고 메모리 누수가 발생합니다.

 

어? apartment,tenant의 참조를 끊어내면 해결할 수 있겠구나 그런데?!!!!  john,unit4A 변수가 nil 이어서 apartment,tenant에 접근을 못하네!! ㅠㅠㅠ

 

이런 상황을 강한 순환 참조(Stron Reference Cycles) 라고 합니다.

 

이 강한 순환 참조를 해결할 수 있는 방법으로는 weak, unowned를 사용합니다.

 

** weak

-> 약한 참조 (Weak References)

-> 참조하고 있는 인스턴스가 먼저 메모리에서 해제될 때 사용

-> Reference Count 는 증가하지 않는다.

 

말로만 설명하면 이해하기 참 어렵죠 ! Swift 공식문서의 예제를 통해서 공부해 봅시다!

앞선 예제와는 달리 tenant 변수가 weak으로 선언된 것을 볼 수 있습니다.

그리고 앞선 예제처럼 Person 인스턴스와 Apartment 인스턴스의 변수에서 각각 인스턴스를 상호 참조하도록 할당합니다.

그러면 아래와 같은 참조 상황을 볼 수 있습니다. 앞선 예제와 다른 점은 Apartment의 tenant변수가 Person 인스턴스를 약한 참조(weak)로 참조하고 있다는 것입니다.

왜? 굳이 tenant 변수만 약한 참조로 설정했을까 ?

weak는 "참조하고 있는 인스턴스가 먼저 메모리에서 해제될 때 사용" 된다고 했죠?

위 예제에서는 Apartment에서 세입자(Person)가 먼저 나가는 상황 즉, 먼저 메모리에서 해제되는 상황이 있을 수 있기 때문에 tenant를 weak 변수로 설정한 것입니다.

 

john을 nil 선언하게 되면

john과 Person 인스턴스의 참조가 끊어지면서 참조 count가 1 -> 0으로 바뀌게 되면서 Person 인스턴스는 메모리에서 해제가 됩니다.

 

##Tip

 - 런타임 시 tenant의 값이 nil로 변경될 수 있기 때문에 항상 Opetional로 선언되어야 한다.

 - 런타임시 tenant 의 값이 nil로 변경될 수 있기때문에 항상 var로 선언되어야 한다.

 

** unowned

-> 미소유 참조 (Unowned References)

-> 참조 대상이 되는 인스턴스가 현재 참조하고 있는 인스턴스와 같은 생명주기를 갖거나 더 긴 생명주기를 가질 때

-> Reference Count는 증가하지 않는다.

-> ARC는 미소유 참조에는 절대 nil을 할당하지 않는다. (옵셔널 타입 X)

 

 

Swift 공식 문서의 예제를 통해 미소유 참조의 동작을 확인해 보겠습니다.  우선 Customer와 CreditCard 두 개의 클래스를 선언합니다.

여기서 Customer는 card 변수로 CreditCard 인스턴스를 참조하고 있고 CreditCard는 customer로 Custome인스턴스를 참조하고 있습니다. customer는 미소유 참조 unowned로 선언합니다.

 

            "참조 대상이 되는 인스턴스가 현재 참조하고 있는 인스턴스와 같은 생명주기를 갖거나 더 긴 생명주기를 가질 때"

고객(참조 대상)과 신용카드(참조하고 있는)를 비교해 봤을 때 신용카드는 없더라도 사용자는 남아있을 것이기 때문입니다. 다시 말하면 사용자는 항상 존재합니다. 그래서 CreditCard에 customer를 unowned로 선언합니다.

 

john이라는 고객을 만들고 john의 신용카드를 만들어줍니다.

john 이라는 고객을 참조하는데 unowned로 하겠다고 명시해주고 있습니다. 

이렇게 되면 Customer 인스턴스와 CreditCard 인스턴스의 RC(참조 횟수)는 1이 됩니다.

 == weak, unowned는 RC를 증가시키지 않는다.(중요!!)

john 변수의 Customer 인스턴스의 참조를 끊게 되면

1. Customer에 대한 강한 참조가 끊어지게 되고

2. CreditCard를 참조하고 있는 개체도 사라지므로 CreditCard 인스턴스도 메모리에서 해제된다.

 

 

이번 포스팅으로 ARC에 대한 개념과 Reference Cycles를 해결하는 방법에 대해 알아봤는데요.

이해가 되지 않는 부분이라던지 틀린 부분 있으면 댓글 부탁드릴게요 ^__^

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

[Swift] 클로저 (Closures) 란?  (0) 2022.01.19
[Swift] ARC 개념 및 weak, unowned 알아보기 (1)  (4) 2022.01.04