ARC

2024. 1. 8. 22:24swift

ARC

  • Swift 는 자동 참조 카운팅(ARC)을 사용하여 앱의 메모리 사용량을 추적하고 관리합니다.
    • 자동으로 메모리를 관리해주는 방식입니다.
    • 개발자가 메모리 관리에 신경을 덜 쓸 수있게 편리하게 해준다.
    • 더이상 필요하지 않는 클래스의 인스턴를 메모리에서 해제하는 방식으로 동작합니다.

ARC 어떻게 동작해요?

  • 새로운 class인스턴스 생성할때마다, ARC는 해당 인스턴스의 정보를 적재하기 위해 메모리의 chunk를 할당한다.
  • 이 메모리는 인스턴스 타입에 대한 정보를 해당 인스턴스와 관련되어 저장된 프로퍼티들의 값을 함께 포함 하여 유지합니다.
  • 인스턴스가 더이상 필요하지 않게 될때, ARC는 해당 인스턴스에 의해 사용되는 메모리를 해제하고 해제된 해당 메모리는 다른 목적을 위해 사용될 수 있습니다.

클래스 인스턴스 간의 강력한 참조 순환

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

image

  • 먼저 두 클래스가 서로 강한 참조를 하고 있어, 순환참조가 발생합니다.
  • 서로 인스턴스를 강하게 참조하여 서로 해제되지 않는 순환 참조이다.
  • 이러한 방법을 해결하기 위해서는 약한 참조, 미소유 참조를 사용해야한다!
약한 참조란?
  • 강한 참조을 방지하기 위해서 사용한다.
  • 약한 참조는 속성 또는 변수 선언에 있어 weak 키워드를 배치하여 나타냅니다.
  • 약한 참조는 참조하는 인스턴스를 강력하게 유지하지 않기 때문에,
    • 따라서 ARC는 참조하는 인스턴스가 할당 취소될 때 약한 참조를 nil로 자동으로 설정됩니다.
    • 또한 약한 참조는 런타임에 값을 nil로 변경할 수 있도록 허용해야 하기 때문에 항상 선택적 형식의 상수가 아닌 변수로 선언됩니다.
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 john: Person?
var unit4A: Apartment?


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


john!.apartment = unit4A
unit4A!.tenant = john

이제는 weak var 인 약한참조가 생겼다!

image

Person에서는 여전히 apartment인스턴스에 강한참조가 있지만 apartment인스턴스에서는 이제 약한 참조가 있습니다. Person 즉, 변수를 nil로 설정하여 john변수가 보유하는 강력한 참조를 해제하면 Person인스턴에 대한 강력한 참조가 더 이상 없습니다.

john = nil

인스턴스에 person 더이상 없기 때문에 할당 취소 tenant-> nil이다.
Apartment 인스턴스에 대해 유일하게 남아 있는 강력한 참조는 unit4A 변수에서 가져온 것입니다. 강력한 참조를 깨면 Apartment 인스턴스에 대한 강력한 참조가 더 이상 없습니다.

unit4A = nil

Apartment에 대한 강한 참조가 없기때문에 할당이 취소됩니다.

image

소유되지 않은 참조

  • 약한 참조와 같이 소유되지 않는 참조는 참조하는 인스턴스를 강력하게 유지하지 않습니다. 그러나 약한 참조와 달리 소유하지 않는 참조는 다른 인스턴스의 수명이 같거나 더 긴 경우에 사용 됩니다.
    • 같은말?일수도? 미소유 참조는 강한 참조를 방지하면서도, 참조하는 인스턴스가 항상 존재할 것이라는 가정 하에 사용된다.
    • unowend키워드를 사용하여 나타냅니다.
class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) {
        self.name = name
    }
    deinit { print("\(name) is being deinitialized") }
}


class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card #\(number) is being deinitialized") }
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
  • customer 인스턴스(john)가 생성되고, creditCard 인스턴스를 참조합니다.
  • john이 nil로 설정되면 Customer 인스턴스는 해제되지만, creditCard인스턴스는 Customer의 생명주기에 영향을 받지 않아 계속 존재합니다.
  • 미소유 참조 특징
    • 미소유 참조는 참조하는 인스턴스의 생명 주기에 영향을 주지 않습니다.
    • 참조하는 인스턴스가 해제되면, 미소유는 참조는 여전히 그 값을 유지함

클로저에 대한 강한 참조 주기

class HTMLElement {
    let name: String
    let text: String?
    lazy var asHTML: () -> String = {
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }
    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}
  • asHTML 클로저가 self를 캡쳐하여 강한 참조가 발생합니다.
    image
  • 캡쳐리스트를 사용하여 강한 참조를 방지합니다.
    • [weak self], [unowned self] 이렇게 사용하여 강한 참조 방지, 클로저가 인스턴스를 강하게 참조하는지 않아 순환 참조가 발생하지 않는다.
 lazy var asHTML: () -> String = { [unowned self] in
 
    }

 

 

'swift' 카테고리의 다른 글

가치와 원칙에 대해서  (0) 2024.04.15
Swift에서 크래시안나게 하는 습관  (0) 2024.04.15
ISP: 인터페이스 분리 원칙  (1) 2023.12.03
LLVM 에 대해서 알아보자.  (0) 2023.09.18
Swift 의 장점에 대해서!  (0) 2023.08.22