iOS/Swift - ARC에 대해 (1)

김영채 (Kevin)·2022년 3월 28일
0

iOS & Swift

목록 보기
105/107

ARC에 대한 개념을 다시 확실하게 잡고 싶어 공식 문서를 보고 정리한 내용을 공유한다.

공식문서 링크 : https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html

  • Swift는 기본적으로 ARC를 통해서 앱의 메모리 사용량을 관리해주는 역할을 한다.

웬만해서는 개발자가 크게 신경 안 써도 잘 작동한다. 자동적으로 클래스 인스턴스 가 필요 없을 때 메모리에서 잘 해제해준다.

그런데 ARC가 이걸 다 해주진 못하고, 앱의 효율적인 메모리 관리를 위해 개발자가 객체와 객체 사이의 관계를 조금 더 명확히 작성해줘야 ARC가 이걸 파악해서 메모리를 해제해야 할 때 해제해준다.

✻ Reference Counting은 클래스 인스턴스에게만 일어난다. 구조체나 enum은 Value Type이기 때문에 주소값, 즉 참조에 대한 정보가 저장되거나 왔다갔다 하지 않는다.

ARC 작동 방식


한 클래스 인스턴스를 생성할 때, ARC는 메모리에 해당 클래스에 대한 참조 정보를 할당한다.

→ 인스턴스의 타입, 가지고 있는 프로퍼티 등을 저장한다.

그리고 해당 객체가 더 이상 쓰일 필요가 없을 때 ARC는 이 객체를 메모리에서 해제한다.

ARC가 해당 객체를 메모리에서 해제할지 말지는 해당 객체에 대한 참조 횟수가 0이냐 아니냐에 달려있다. 참조 횟수가 1이상이면 메모리에서 해제하지 않는다.

→ 특정 클래스 인스턴스를 어떤 변수나 상수에 할당을 하면 해당 클래스에 대한 strong reference를 만드는 것이다. strong reference가 있는 한 메모리에서 해제되지 않는다.

ARC 예시


reference1 = Person(name: "Kevin")

reference2 = reference1
reference3 = reference1

✻ Person 인스턴스에 대한 strong reference가 3개 있음.

reference1 = nil
reference2 = nil

→ 참조 횟수 - 2 → 아직 1개가 남아있기 때문에 Person 인스턴스는 메모리에서 해제되지 않고, 그에 따라 deinit { } 메서드는 호출되지 않음.

reference3 = nil

→ 비로소 deinit { } 메서드가 호출되면서 메모리에서 해제

클래스 인스턴스 사이의 Strong Reference Cycle


위 예시 같은 경우에는 ARC가 참조 횟수를 추적하면서 그 횟수가 0에 도달했을 때 알아서 잘 메모리에서 해제해준다.

다만, 어떤 경우에는 클래스 참조 횟수가 0에 절대 도달하지 않는 경우 가 있다.

✻ 언제? → 2개의 클래스 인스턴스가 서로에 대한 “강한 참조”를 가지고 있으면 strong reference cycle이 일어나면서 메모리에서 절대 해제가 되지 않는다.

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john = Person(name: "John Appleseed")
var unit4A = Apartment(unit: "4A")

두 개의 인스턴스를 생성하면 strong reference는 아래와 같은 형태가 된다.

john 변수는 Person 인스턴스에 대한 강한 참조가 1개 있고, unit4A 변수는 Apartment 인스턴스에 대한 강한 참조가 1개 있다. 각각 1개씩이다.

john.apartment = unit4A
// Person 인스턴스가 Apartment 인스턴스에 대한 strong reference가 1 증가했다
unit4A.tenant = john
// Apartment 인스턴스가 Person 인스턴스에 대한 strong reference가 1 증가했다

→ 이제 총 2개씩의 strong reference를 가지고 있음.

john = nil
unit4A = nil

이렇게 해도 메모리에서 해제가 되지 않는다.

→ 해제를 해도 아래와 같이 강한 참조가 남아있음

✻ 그런데 john 가 unit4A를 해제했기 때문에, 이제 그 안에 있는 apartment랑 tenant 프로퍼티에 접근할 수 있는 법이 사라진거임. 접근이 불가능해서 결국 memory leak 발생

profile
맛있는 iOS 프로그래밍

0개의 댓글