# ARC - Swift 는 자동 참조 카운팅(ARC)을 사용하여 앱의 메모리 사용량을 추적하고 관리합니다. - 자동으로 메모리를 관리해주는 방식입니다. - 개발자가 메모리 관리에 신경을 덜 쓸 수있게 편리하게 해준다. - 더이상 필요하지 않는 클래스의 인스턴를 메모리에서 해제하는 방식으로 동작합니다. ### ARC 어떻게 동작해요? - 새로운 `class`인스턴스 생성할때마다, ARC는 해당 인스턴스의 정보를 적재하기 위해 메모리의 `chunk`를 할당한다. - 이 메모리는 인스턴스 타입에 대한 정보를 해당 인스턴스와 관련되어 저장된 프로퍼티들의 값을 함께 포함 하여 유지합니다. - 인스턴스가 더이상 필요하지 않게 될때, ARC는 해당 인스턴스에 의해 사용되는 메모리를 해제하고 해제된 해당 메모리는 다른 목적을 위해 사용될 수 있습니다. ### 클래스 인스턴스 간의 강력한 참조 순환 ```swift 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](https://hackmd.io/_uploads/rJQDToNL6.png) - 먼저 두 클래스가 서로 강한 참조를 하고 있어, 순환참조가 발생합니다. - 서로 인스턴스를 강하게 참조하여 서로 해제되지 않는 순환 참조이다. - 이러한 방법을 해결하기 위해서는 약한 참조, 미소유 참조를 사용해야한다! ##### 약한 참조란? - 강한 참조을 방지하기 위해서 사용한다. - 약한 참조는 속성 또는 변수 선언에 있어 `weak` 키워드를 배치하여 나타냅니다. - 약한 참조는 참조하는 인스턴스를 강력하게 유지하지 않기 때문에, - 따라서 ARC는 참조하는 인스턴스가 할당 취소될 때 약한 참조를 nil로 자동으로 설정됩니다. - 또한 약한 참조는 런타임에 값을 nil로 변경할 수 있도록 허용해야 하기 때문에 항상 선택적 형식의 상수가 아닌 변수로 선언됩니다. ```swift 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](https://hackmd.io/_uploads/rJzKpsEU6.png) Person에서는 여전히 apartment인스턴스에 강한참조가 있지만 apartment인스턴스에서는 이제 약한 참조가 있습니다. Person 즉, 변수를 nil로 설정하여 john변수가 보유하는 강력한 참조를 해제하면 Person인스턴에 대한 강력한 참조가 더 이상 없습니다. ```swift john = nil ``` 인스턴스에 person 더이상 없기 때문에 할당 취소 tenant-> nil이다. Apartment 인스턴스에 대해 유일하게 남아 있는 강력한 참조는 unit4A 변수에서 가져온 것입니다. 강력한 참조를 깨면 Apartment 인스턴스에 대한 강력한 참조가 더 이상 없습니다. ```swift unit4A = nil ``` Apartment에 대한 강한 참조가 없기때문에 할당이 취소됩니다. ![image](https://hackmd.io/_uploads/BkMcRsEI6.png) ### 소유되지 않은 참조 - 약한 참조와 같이 소유되지 않는 참조는 참조하는 인스턴스를 강력하게 유지하지 않습니다. 그러나 약한 참조와 달리 소유하지 않는 참조는 다른 인스턴스의 수명이 같거나 더 긴 경우에 사용 됩니다. - 같은말?일수도? 미소유 참조는 강한 참조를 방지하면서도, 참조하는 인스턴스가 항상 존재할 것이라는 가정 하에 사용된다. - `unowend`키워드를 사용하여 나타냅니다. ```swift 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의 생명주기에 영향을 받지 않아 계속 존재합니다. - 미소유 참조 특징 - 미소유 참조는 참조하는 인스턴스의 생명 주기에 영향을 주지 않습니다. - 참조하는 인스턴스가 해제되면, 미소유는 참조는 여전히 그 값을 유지함 #### 클로저에 대한 강한 참조 주기 ```swift 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](https://hackmd.io/_uploads/rJ5o82VLT.png) - 캡쳐리스트를 사용하여 강한 참조를 방지합니다. - [weak self], [unowned self] 이렇게 사용하여 강한 참조 방지, 클로저가 인스턴스를 강하게 참조하는지 않아 순환 참조가 발생하지 않는다. ```swift lazy var asHTML: () -> String = { [unowned self] in 냉무 } ```