Search
📲

ARC(Automatic Reference Counting)

생성일
2024/11/20 11:19
태그

1. 보통 알고 있는 수준의 ARC(Automatic Reference Counting)

ARC (Automatic Reference Counting)는 Swift와 Objective-C에서 메모리를 자동으로 관리하여, 참조 카운트를 기반으로 객체를 할당 및 해제한다.
객체의 참조 카운트가 0이 되면 메모리가 자동으로 해제되며, 개발자가 retain/release를 수동으로 관리할 필요가 없다. 순환 참조(Circular Reference)를 방지하려면 weak 또는 unowned 참조를 사용해야 합니다.
보통 ARC를 이정도로 이해하고 있고, 사실 이정도의 지식만 알아도 서비스 개발에 큰 문제는 없습니다. 다만, 일부 특수한 케이스 대응을 위해 iOS에서 메모리를 사용하는 방법을 이해하고 있는 것이 좋습니다.

2. Objective-C의 ARC, Swift의 ARC는 같다?

인스턴스를 생성하고 참조 카운트를 print하는 로직을 Objective-C와 Swift로 각각 구현하고 실행시켜보겠습니다.
Objective-C 코드(눌러서 펼쳐보기)
Objective-C 실행 결과
Reference count of A: 1 Reference count of B: 2 AClass deinitialized BClass deinitialized
Plain Text
복사
코드 해설
AClass *aInstance = [[AClass alloc] init]; // aInstance가 생성되며 참조카운트 1을 갖습니다. BClass *bInstance = [[BClass alloc] init]; // bInstance가 생성되며 참조카운트 1을 갖습니다.
Objective-C
복사
aInstance와 bInstance의 참조 카운트는 현재 Scope(ViewDidLoad)를 벗어나면 감소되어 0이 되고 곧 소멸되어 dealloc 소멸자가호출됩니다.
aInstance.bInstance = bInstance;
Objective-C
복사
bInstance는 aInstance의 프로퍼티에 강한 참조되어 참조카운트가 1 증가합니다.
Swift 코드(눌러서 펼쳐보기)
Swift 실행 결과
Reference count of A: 2 Reference count of B: 3 AClass deinitialized BClass deinitialized
Plain Text
복사
결과를 분석해보면 Swift가 참조 카운트가 1씩 크게 잡히게 됩니다. 왜?
ChatGPT한테 이유를 물어보고 그 이유가 타당한지 추가 실험을 진행해봅시다.
CFGetRetainCount 호출 중 참조 카운트가 1 증가하는 이유는 Swift의 ARC가 런타임 안정성을 보장하기 위해 추가적으로 retain을 호출하기 때문입니다. 이 동작은 CFGetRetainCount 같은 Core Foundation 함수와 상호작용할 때 발생하며, ARC의 기본 설계에 따른 정상적인 현상입니다.
위 답변을 보고 CFGetRetainCount 뿐 아니라 Swift에서 ObjC함수를 호출하는 경우 안정성 보장을 위해 retain을 진행한다 라는 가설이 생겼습니다.
우선 함수에 대해서 더 실험을 진행해봅니다.
Objective-C 함수 내부에서의 참조 카운트 실험 코드
인스턴스의 참조카운트를 출력하는 함수 3개를 만들어 함수와 콜스택에 따라 어떻게 참조카운트가 바뀌는지 살펴봅니다.
ObjC 실행 결과
Reference count of A: 1 Reference count of A: 2 in function1 Reference count of A: 3 in function2 👉 콜스택이 쌓이면서 참조 카운트 증가 Reference count of A: 4 in function3 👉 콜스택이 쌓이면서 참조 카운트 증가 --- 👉 함수가 종료되면 증가되었던 참조 카운트가 감소됨 Reference count of A: 2 in function1 Reference count of A: 3 in function2 Reference count of A: 4 in function3 --- Reference count of A: 2 in function1 Reference count of A: 3 in function2 Reference count of A: 4 in function3 AClass deinitialized
Plain Text
복사
Objective-C 함수 내부에서 참조 카운트가 증가되며, 함수가 종료되면 참조 카운트가 감소됨을 알 수 있습니다.
Swift 함수 내부에서의 참조 카운트 실험 코드
Swift 실행 결과
Reference count of A: 2 Reference count of A: 2 in function1 Reference count of A: 2 in function2 Reference count of A: 2 in function3 --- Reference count of A: 2 in function1 Reference count of A: 2 in function2 Reference count of A: 2 in function3 --- Reference count of A: 2 in function1 Reference count of A: 2 in function2 Reference count of A: 2 in function3 AClass deinitialized
Swift
복사
Swift에서는 함수 파라미터로 넘기더라도 참조카운트가 증가하지 않습니다.
즉, Objective-C에서는 함수 파라미터로 전달하는 과정에서 참조 카운트가 증가하고 Swift는 증가하지 않는 차이가 있습니다.

3. Swift에서 Objective-C 함수를 사용하는 경우에는?

아직 2번 실험에서 swift에서 CFGetRetainCount() 함수를 사용했을 때 예상보다 1 크게 나오는 문제에 대한 가설을 검증하지 못했습니다.
가설 검증을 위해 참조 카운트를 출력하는 Objective-C 함수를 만들고, 이 함수를 swift에서 호출했을 때 어떤 현상이 생기는지 확인해보겠습니다.
ObjC m 코드
함수 호출에 따라 참조 카운트 증가도 함께 확인하기 위해 3개의 함수를 추가합니다.
swift에서 objective-c함수를 호출하기 위해 브릿지 헤더도 추가합니다.
bridge-header.h 코드
swift에서 objective-c함수를 호출하고 결과를 살펴봅니다.
swift 프로젝트 코드
결과
Retain Count1: 3 👉 예상보다 2증가 Retain Count2: 4 👉 objc에서는 함수 파라미터로 사용시 Retain Count3: 5 AClass deinitialized
Plain Text
복사
* 함수 중첩에 따라 참조 카운트가 증가하는 것으로 보아 swift에서 objc 함수를 호출하더라도, objc의 특성(함수 파라미터로 전달되는 인스턴스의 참조 카운트 증가)는 유지된다.
* 위 증명에 따라 Retain Count1의 값은 2이어야 하나 1이 더 증가한 것으로 보아 1번 가설(Swift에서 Objective-C함수 호출시 1증가) 도 함께 작용한 것으로 확인됨.

3. 참조 카운트가 0이 된 인스턴스 (ARC와 autorelease)

ARC를 설명할 때 참조 카운트가 0이 되면 메모리가 할당 해제된다고 표현합니다. 그러면 참조 카운트가 0이 되는 순간 바로 dealloc 되는걸까요?