[iOS] ARC

Zoe·2023년 12월 5일
0

iOS

목록 보기
28/39

오늘은 ARC를 swift 공식 문서를 활용하여 이해해보자.

ARC는 Automatic Reference Counting 이다.

말 그대로 자동으로 메모리를 관리해준다는 의미이다.

과거 MRC를 사용할 때는 개발자가 직접 메모리를 관리해줄 필요가 있었지만, ARC가 나타난 이후부터는 retain, release를 관리해주기 때문에 메모리 관리에 대한 어려움이 줄어들게 되었다.

간단한 예제와 함께 ARC 작동 방식을 살펴보자

class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}
var reference1: Person?
var reference2: Person?
var reference3: Person?

위 reference1, reference2, reference3는 옵셔널 Person 타입이므로 아직 초기화되지 않았다.

reference1 = Person(name: "Zoe")
// Prints "Zoe is being initialized"

위 처럼 인스턴스를 생성했을 때 값이 초기화된다.
이때부터 메모리 관리를 시작한다. ARC는 강한 참조가 생겼으므로 메모리가 유지되고, 할당이 취소되지 않도록 관리한다.

reference2 = reference1
reference3 = reference1

이제 한 개의 인스턴스에 대한 3개의 강한 참조가 생겼다.

reference1 = nil
reference2 = nil

reference1, reference2에 nil을 할당해도 아직 reference3의 참조가 남아있기 때문에 아직 메모리 할당을 해제하지 않는다.

reference3 = nil
// Prints "Zoe is being deinitialized"

이제 모든 강한 참조가 사라졌으므로 메모리 할당이 해제된다.

이처럼 ARC는 인스턴스에 대한 reference counting을 추적하고, 더 이상 필요하지 않은 인스턴스의 메모리를 해제할 수 있다.

이렇게 간단히 메모리 관리를 할 수 있다면 좋겠지만,
메모리 관리를 위해 고려해야 할 점이 있다.

바로 강한 순환 참조이다.

강한 순환 참조란 두 가지 이상의 객체가 서로에 대한 Strong Reference(강한 참조) 상태를 가지고 있을 때 발생한다.
강한 순환 참조가 발생하게 되면 서로에 대한 참조가 해제되지 않기 때문에 메모리가 유지되고, 이로 인해 Memory Leak이 발생하게 된다.

간단한 예제를 통해 알아보자.

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") }
}

Person 클래스에서 apartment는 옵셔널이다. 마찬가지로 Apartment 클래스에서 tenant도 옵셔널이다.

var zoe: Person?
var unit4A: Apartment?
zoe = Person(name: "Zoe")
unit4A = Apartment(unit: "4A")

인스턴스를 할당하자 마자 두 변수 모두 강한 참조를 가진다.

zoe!.apartment = unit4A
unit4A!.tenant = zoe

이 때 강한 참조 순환이 생긴다.

zoe = nil
unit4A = nil

nil로 할당해줘도 둘 사이에 있는 참조가 그대로 존재해서 메모리가 해제되지 않는다.

그렇다면 강한 순환 참조를 어떻게 해결할까?

바로 약한 참조 사용하기 !

weak 키워드를 사용해서 약한 참조를 하고, 강한 순환 참조를 막을 수 있다. 약한 참조는 언제든지 해제될 수 있기 때문에 reference count를 증가시키지 않는다.

위의 예제를 약한 참조를 사용해서 수정해보자.

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 }
    weak var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var zoe: Person?
var unit4A: Apartment?


zoe = Person(name: "Zoe Appleseed")
unit4A = Apartment(unit: "4A")


zoe!.apartment = unit4A
unit4A!.tenant = zoe

zoe의 apartment는 여전히 강한 참조를 가지고 있지만, unit4A의 tenant는 Person 인스턴스에 대하여 약한 참조를 가지게 되었다.

zoe = nil
// Prints "Zoe is being deinitialized"

zoe에 nil을 할당하는 순간 더이상 강한 참조가 없기 때문에 바로 deallocate하게 된다.

unowned와 클로저에서의 강한 순환 참조는 다음에 알아보자.

profile
iOS 개발자😺

0개의 댓글