iOS

ARC & Retain Cycle 예제로 살펴보기 - iOS

나른한코딩 2022. 4. 17. 19:36

 

 

편의상 RC(Reference Count, 참조카운트) 라고 작성하였습니다.

예제를 진행하기 전에 혹은 아래의 글을 읽다가

 

 '클래스, 인스턴스, 객체'가 무엇인지 잘 모르겠다면 여기를 !

'strong, weak, unowned' 키워드가 무엇인지 잘 모르겠다면 여기를 !

'ARC와 RetainCycle'이 무엇인지 잘 모르겠다면 여기를 !

 

 


 

 

swift파일을 하나 생성하여

모델을 생성 아래와 같이 생성하였습니다!

 

두 모델 Person, Mac은 서로의 타입의 프로퍼티를 가지고 있습니다.

(사람은 맥북을 소유하고있고, 맥북은 주인이 존재한다! 의 느낌입니다.)

Person, Mac 클래스 생성

 

 

 

 

그리고 다시 ViewController로 넘어와서

다음과 같이 각각 Person, Mac 타입의 변수를 선언해주고

객체 선언

시작을 해봅시다!

 

 

 

 

 


[ 서로를 참조하지 않을 때 ]

서로를 참조하는 프로퍼티에는 nil값을 대입해 객체를 생성하고 

위에서 선언한 변수에 대입해주었습니다.

 

이때 아래와 같이

keenm1Pro 에 각각 nil을 대입하여 메모리에서 해제 시키면

어떻게 될까요??

 

객체 생성

보기를 드리겠습니다. 

1. deinit이 호출되며 print문이 출력된다.
2. 아무 일도 일어나지 않는다.

 

.

.

.

.

 

 

결과는?

실행 결과 1

두 객체 모두 deinit의 print문이 정상적으로 출력되면서

메모리에서 잘 해제되었음을 유추할 수 있죠.

 

 

지금의 관계를 그림으로 표현해보면 다음과 같습니다.

 

 

  • keen에 Person 인스턴스를 대입할 때 RC가 1 증가합니다. (RC +1)
    ( -> 생성한 Person 클래스 인스턴스의 메모리 주소를 참조하고 있기에 )
  • Person의 mac과 Mac의 owner는 각각 nil이 할당되어있기 때문에 서로를 참조하고 있지않아 RC가 증가하지 않고
  • 각각의 인스턴스에 nil을 할당하면 (RC -1)
  • RC가 0이 되며 메모리에서 정상적으로 해제가 됩니다.

 

 

 

 


 

[ 서로를 참조할 때 ]

그럼 이번엔 두 객체를 참조하는 프로퍼티에 값을 줘볼까요?

 

서로를 강하게 참조하는 경우

 

 

assignProperties()라는 함수를 만들어 

createaObjects()를 한 다음에 nil이 었던 macowner 프로퍼티에 서로의 객체를 대입해줍니다.

 

 

 

실행 하면 어떻게 될까요?

아까와 보기는 같습니다.

1. deinit이 호출되며 print문이 출력된다.
2. 아무 일도 일어나지 않는다.

 

.

.

.

.

 

 

 

 

 

 

실행 결과는 아래와 같습니다.

아무리 기다려도 아무 것도 프린트 되고 있지 않죠?

이 말은 deinit이 되지 않았다는 것인데요

(왜나면 deinit이 되면 print문이 출력되어야 하니까)

 

출력 결과 2

 

 

 

 

다음을 그림으로 그려보면

이유를 이해하기 쉬울 것입니다.

 

 

 

 

서로를 참조하고 있는 경우

 

Person의 인스턴스인 keen m1Pro를 참조하고 있고

Mac의 인스턴스인 m1Pro keen을 참조하고 있습니다.

 

이렇게 서로를 참조하고 있을 때 

RC는 2가 됩니다.

 

 

  • keen에 Person클래스 인스턴스를 대입합니다. (RC +1)
  • Person인스턴스의 mac프로퍼티에 m1Pro 인스턴스를 대입합니다. (RC +1)
  • keen = nil 을 대입하여 메모리에서 해제시키려합니다. (RC -1)

(m1Pro의 경우도 같습니다.)

 

 

최종적으로 RC = 1 이 남게 됩니다.

RC가 0이 아니면 ARC는 메모리에서 해제 시키지 않습니다. 

 

 

 

 


 

[ 강한 순환 참조를 막는 방법은? ]

 

결론 부터 말하면 weak, unowned 키워드를 사용하면 됩니다.

지금은 weak을 사용해볼게요

 

 

더보기

[ unowned 키워드에 대해 간단 설명 ]

 

- weak 과 마찬가지로 RC를 증가시키지 않습니다.

- unowned는 다른 인스턴스와 생명주기가 같거나 더 긴 경우에 사용하곤 합니다.

- weak은 nil일 수도 아닐 수도 있기에 옵셔널 값을 갖고

   unwoned는 항상 값을 가지고 있다고 가정하고 nil이 될 수 없다는 차이점이 있습니다.

 

ownerweak으로 선언하면 RC를 증가시키지 않기 때문에

keen이 메모리에서 해제가 되면 m1Proowner도 자동으로 해제될 것입니다.

 

 

 

직접 확인해보겠습니다

weak 으로 선언하기

 

서로를 강하게 참조하고 있기 때문에 생기는 문제이므로

owner 프로퍼티를 weak으로 선언해주어

RC를 증가시키지 않고 메모리에서 해제되면 사라지도록 해주면 됩니다.

 

 

keen을 메모리에서 해제시 m1Pro의 owner 프린트해서 확인해보기

 

다음과 같이 keennil을 할당했을 때

메모리에서 잘 해제가 되었음을 알 수 있는 deinit의 print문이 출력되었고

m1Proowner 프로퍼티 또한 nil이 되어있는 것을 확인할 수 있습니다.

(weak으로 선언하지 않은 상태에서 m1Pro?.owner를 프린트해보면 더 이해가 잘 되실겁니다.)

 

 

 

 

 

필요시 프로젝트를 업로드한 깃헙 링크를 참고해주세요 :)

 

 

 

반응형