내일배움캠프 iOS

iOS) TIL #22 ARC ( Automatic Reference Counting)

yjuni22 2024. 11. 19. 21:00

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting/

 

Documentation

 

docs.swift.org

 

ARC란

  • Reference Type ( 클래스, 클로저 ) 의 인스턴스 메모리 관리를 자동으로 해주는 기능
    • Value Type의 인스턴스는 ARC가 관리하지 않습니다.
  • 메모리 관리를 직접 하지 않아도 ARC가 자동으로 메모리 관리를 처리합니다.
    • 인스턴스가 더이상 필요하지 않을 때 메모리에서 자동으로 해제됩니다.
  • Reference Type의 인스턴스를 참조할 때, 참조카운트가 증가하며, 이를 strong 참조라고 합니다.(default)
  • 참조카운트의 증가를 원하지 않는 경우 weak 또는 unowned 참조를 사용할 수 있습니다.

 

ARC의 동작 방식

 

1. 인스턴스 생성과 메모리 할당

  • class의 새로운 인스턴스를 생성할 때 마다 ARC 는 해당 인스턴스에 대한 정보를 저장할 메모리 공간을 할당합니다.
  • 이 공간에는 인스턴스의 타입 정보와 프로퍼티의 값이 저장됩니다.

2. 참조 카운트 관리

  • ARC는 사용중인 인스턴스가 메모리에서 해제되지 않도록, 몇개의 프로퍼티, 상수, 변수가 인스턴스를 참조하고 있는지 추적합니다.
    • 참조 시작 시 : 카운팅 + 1
    • 참조 종료 시 : 카운팅 - 1
    • 최종적으로 참조카운트이 0이 되면 인스턴스는 메모리에서 해제됩니다.

3. 메모리 해제

  • 참조카운트가 0이 된 인스턴스는 더이상 필요하지 않기 때문에, ARC가 해당 인스턴스가 사용했던 메모리를 해제합니다.
    • 만약 참조 카운트가 0이 되기전에 인스턴스가 해제되면, 이후 그 인스턴스에 접근할 경우 앱이 크래시가 발생할 수 있습니다.

ARC 동작 시기

ARC는 컴파일타임에 인스턴스의 참조 카운트를 증가 또는 감소시키는 코드를 자동으로 삽입합니다.

 

메모리 누수 Memory Leak

  • 사용이 끝난 인스턴스가 메모리에서 해제되지 않고 남아있는 현상입니다.
  • 이렇게 메모리 공간이 불필요하게 사용되면 성능 저하가 발생할 수 있습니다.

강한순환참조(Strong Reference Cycle)

  • iOS에서 자주 발생하는 Memory Leak의 원인중 하나입니다.

 

 

두 인스턴스를 생성하고 각각 강한참조로 참조하고 있는 상태(Default가 strong이기 때문에)

 


 

 

Person instance와 Apartment instance 가 서로 강한 참조로 사용

 

 

- Person instance와 Aparment instance의 RC가 1로 사용이 종료되었지만 메모리에서 해제되지 않음.

- 강한 참조로 서로를 참조한다고 하여 강한순환참조라고 합니다.

 

강한순환참조 해결방법

  • 참조 카운트를 올리지 않으면 강한순환참조를 방지할 수 있습니다.
  • 참조 카운트를 올리지 않기 위해서는 strong이 아닌 weak, unowned를 사용하면 됩니다.
class Person {
    let name: String
    init(name: String) { self.name = name }
    weak 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 brody: Person?
var unit100A: Apartment?

brody = Person(name: "brody") // Person instance RC : 1
unit100A = Apartment(unit: "100평 아파트") // Apartment instance RC : 1


brody.apartment = unit100A // Person의 apartment가 weak 이기 때문에 RC가 올라가지 않음 RC : 1
unit100A.tenant = brody // Apartment의 tenant가 weak이기 때문에 RC가 올라가지 않음 RC : 1

brody = nil // Person instance RC 1 감소 -> RC : 0
unit100A = nil // Apartment instance RC에 1 감소 -> RC : 0
// RC가 0이 되어서 Person instance 메모리에서 해제, Apartment instance 메모리에서 해제