ARC
참조 타입은 하나의 인스턴스가 참조를 통해 여러 곳에서 접근하기 때문에 언제 메모리에서 해제되는지가 가장 중요하다. 인스턴스가 적절한 시점에 메모리에서 해제되지 않는다면 한정적인 메모리 자원을 낭비하게 되고, 이것은 성능 저하로 이어진다.
Swift는 프로그램의 메모리 사용을 관리하기 위해서 메모리 관리 기법인 ARC
(Automatic Reference Counting)를 사용한다.
ARC와 값 타입
ARC
가 관리해주는 참조 횟수 계산(Reference Counting)은 참조 타입인 클래스의 인스턴스에만 적용된다. 구조체나 열거형은 값 타입으로 참조 횟수 계산과 무관하다. 즉, 값 타입은 다른 곳에서 참조하지 않기 때문에 ARC
로 관리할 필요가 없다.
ARC
는 자동으로 메모리를 관리해주는 방식이다. 메모리 관리는 Swift에서 자동으로 작동하기 때문에, 개발자는 메모리 관리에 대해 신경을 덜 쓸 수 있다. ARC
는 더 이상 필요하지 않은 클래스의 인스턴스를 메모리에서 해제하는 방식으로 동작한다.
- 자동으로 메모리 관리해준다.
- 객체에 대한 참조 카운트를 관리하고 0이 되면 자동으로 메모리 해제한다.
- 런타임에 계속 실행되는게 아닌, 컴파일타임에 실행한다.
- 하지만
retain cycle
에는 유의해야 한다. Objective-C
에서는MRC
, 즉 수동으로 메모리 관리를 했다.
ARC 인스턴스 생성 및 해제
Swift에서 ARC
를 이용해 자동으로 메모리 관리를 받기 위해서는 몇 가지 규칙을 알아야 한다. 그 이유는 컴파일과 동시에 인스턴스를 메모리에서 해제하는 시점이 ARC
를 결정하기 때문이다.
ARC
는 클래스의 인스턴스를 생성할 때마다 그 인스턴스에 대한 정보를 저장하기 위한 메모리 공간을 따로 또 할당함- 이 메모리 공간에는 인스턴스의 타입 정보와 관련된 저장 프로퍼티의 값을 등을 저장한다.
- 그 후, 인스턴스가 더 필요 없는 상태가 되면 이 공간을 다른 용도로 활용하기 위해
ARC
가 메모리에서 인스턴스를 삭제한다. - 만약 아직 더 사용해야 하는 인스턴스를 메모리에서 해제시킨다면
- 인스턴스의 프로퍼티에 접근하거나, 인스턴스의 메서드를 호출할 수 없다.
- 만약 강제로 접근하게 된다면 앱이 강제 종료된다.
따라서 ARC
는 해당 인스턴스를 참조하는 다른 인스턴스의 프로퍼티나 변수, 상수 등의 개수를 세고, 해당 인스턴스에 대한 활성 참조가 1개 이상 존재하는 한 인스턴스를 메모리에서 없애지 않는다.
강한 참조
인스턴스가 언제 메모리에서 해제될지 예측 가능하도록 ARC에 적용되는 몇 가지 규칙을 알아야 보려 한다. 그리고 이 규칙을 알지 못할 때 벌어질 수 있는 문제점과 해결방안도 알아볼까 한다.
인스턴스를 메모리에 유지시키려면 ARC
가 해당 인스턴스를 해제하지 않고 유지해야 하는 명분을 제공해야 하는데, 이것이 바로 강한 참조(Strong Reference
)이다.
인스턴스는 참조 횟수가 0이 되는 순간 메모리에서 해제된다.
인스턴스를 다른 인스턴스의 프로퍼티나 변수, 상수 등에 할당할 때 강한 참조를 사용하게 되면 참조 횟수가 +1 증가 하게 된다.
참조의 기본은 강한참조 이기 때문에 별도의 식별자를 명시하지 않으면 강한참조를 한다.
참조
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
Person
클래스는 인스턴스의 name
프로퍼티를 초기화하고 프린트하는 이니셜라이저와 인스턴스가 해제되었음을 알리는 디이니셜라이저를 가지고 있다.
var reference1: Person?
var reference2: Person?
var reference3: Person?
그리고 3개의 Person?
옵셔널 타입 변수가 선언되어 있으며, 해당 변수들은 옵셔널 타입이기 때문에 아직은 Person
인스턴스를 참조하고 있지 않고, nil
값으로 초기화된 상태이다.
이제 새로운 Person
인스턴스를 생성해서 위 세 개의 변수 중에 하나에 넣어보면
reference1: Person(name: "서근")
// 서근 is being initialized
// 인스턴스 참조 횟수 : 1
새로운 Person
인스턴스는 처음 메모리에 생성되면 이니셜라이저가 호출되며 메시지를 출력한다. 그 이후 reference1
에 할당된 Person
클래스 타입의 인스턴스는 강한 참조로 reference1
에 할당되므로, Person
인스턴스의 참조 횟수가 +1
증가된다.
reference2 = reference1
// 인스턴스 참조 횟수 : 2
reference3 = reference1
// 인스턴스 참조 횟수 : 3
그 후, reference2
에 강한참조로 할당되어 참조 횟수 +1
, 이 인스턴스가 reference3
에 할당할 때도 참조 횟수 +1
이 증가한다. 그 결과 총 인스턴스 참조 횟수가 3
이 된다.
이제 하나의 Person
인스턴스가 3
개의 변수에 강한 참조가 되고 있고, 참조 횟수는 3
이 됐다. 따라서 계속 메모리에 살아있게 된다.
해제
reference3 = nil // 인스턴스 참조 횟수 : 2
reference2 = nil // 인스턴스 참조 횟수 : 1
reference1 = nil // 인스턴스 참조 횟수 : 0
// 서근 is being deinitialized
변수에 차례로 nil
을 할당하면 참조 횟수가 감소한다.
마지막에 참조되었던 reference3
에서 제일 먼저 인스턴스 참조를 그만두었기 때문에 참조 횟수는 1
감소하여 2
가 됐고, 마찬가지로 reference2
와 reference1
에서 순차적으로 참조를 그만두면 참조 횟수가 -1
씩 차감되어 0이 된다.
이 참조 횟수가 0
이 되는 그 순간 인스턴스는 ARC
규칙에 의해 메모리에서 해제되며 메모리에서 해제되기 직전!! 디이니셜라이저를 호출한다.
읽어주셔서 감사합니다 🤟
'SWIFT > Grammar' 카테고리의 다른 글
Swift : 고급문법 [ARC 메모리 관리 3 - 약한참조(weak)] (0) | 2022.02.13 |
---|---|
Swift : 고급문법 [ARC 메모리 관리 2 - 강한 참조 순환 문제] (0) | 2022.02.12 |
Swift: 기초문법 [데이터 타입 - Int와 UInt (feat.카멜케이스)] (4) | 2022.02.09 |
Swift: 주요 함수 [max, min, numericCast, precondition, preconditionFailure, print] (0) | 2022.02.08 |
Swift: 주요 함수 [abs, assert, assertionFailure, debugPrint, dump, fatalError] (0) | 2022.02.08 |