Auto Referencing Counting
Auto Referencing Counting(이하 ARC)는 자동으로 메모리를 관리하는 메커니즘이다. 객체에 대한 참조 수를 관리하고 특정 객체의 참조 수가 0이 되었을 때 해당 객체를 메모리에서 해제한다. JVM 진영에서는 Garbage Collector(이하 GC)와 유사하지만 다른 점이 많다. ARC는 컴파일 타임에 결정되지만, GC는 런타임에 수행된다.
Objective-C에서는 메모리 관리를 수동으로 수행했지만, 최근 버전에는 ARC를 지원하며, Swift는 ARC를 자동으로 지원한다.
ARC가 동작하는 원리
위에서 언급한 것 처럼 Objecitve-C는 메모리 관리를 수동으로 했다. 이 때, 참조 수를 증가시키기 위해 retain
, 감소시키기 위해 release
키워드를 사용했다. ARC는 이를 컴파일 타임에 자동으로 적절한 위치에 삽입시켜준다. 이 과정에서 개발자는 작성해야 될 코드 수가 줄어들고, 메모리 관리에 대한 걱정이 줄어든다.
좀 더 디테일한 객체의 메모리 관리는 다음과 같다.
- 동적 할당으로 객체가 생성되면, Heap Object에서 관리된다.
- Heap Object는 객체와 Reference Count를 관리한다.
이 과정에서 autorelease pool이라는 개념도 존재한다. autorelease pool은 ARC에 의해 autorelease된 객체가 등록되는 공간이다. pool이 해제될 때 실제로 객체가 메모리에서 소멸된다.
Strong Reference Cycles
위의 ARC에 의해 개발자가 메모리 관리에 대해 전혀 신경을 쓰지 않아도 되는 것은 아니다. 그 대표적인 예가 Strong Reference Cycle이다. 이는 메모리에 존재하는 두 객체가 서로를 참조하고 있을 때 발생한다.
class Person {
let name: String
var apartment: Apartment?
init(name: String) {...}
deinit {print("\(name) is deinitialized)}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {...}
deinit {print("\(unit) is deinitialized)}
}
Apartment와 Person 클래스가 존재하는 상황이다. 만약, 두 객체가 초기화된 뒤 다음과 같은 코드가 있다고 생각해보자.
let john = Person("John")
let unit4 = Apartment("Unit4")
john.apartment = unit4
unit4.tenant = john
이 과정에서 서로에 대한 강한(strong) 참조가 생긴다. 단방향적인 참조는 문제가 없지만, 양방향적인 참조가 생긴다.
이 때, 아래를 수행하면 메모리에서 해제되지 않는 것을 확인할 수 있다.
john = nil
unit4A = nil
이를 해결하기 위해 weak
키워드를 사용할 수 있다. 이 때, 더 수명이 짧은 인스턴스에 weak(약한 참조)
를 사용해야된다. 먼저, 할당이 해제될 수 있기 때문이다. 그렇기 때문에 바뀐 코드는 다음과 같다.
class Apartment {
let unit: String
weak var tenant: Person?
init(unit: String) {...}
deinit {print("\(unit) is deinitialized)}
}
이제 아래를 수행하면, John이라는 Person 객체도 사라지고, Apartment는 약한 참조를 갖고 있었기 때문에 tenant가 nil로 설정된다.
john = nil
이 과정에서 유의할 점은 weak
는 언젠가 nil로 변환된 가능성를 내포하고 있기 때문에 var 타입과 Optional로 설정되어야한다.
'iOS' 카테고리의 다른 글
[iOS] Dispatch Queue (0) | 2023.01.07 |
---|---|
[iOS] Weak, Strong Reference (0) | 2022.05.15 |