[iOS] ARC

Oxong·2021년 10월 7일
2

iOS CS Study

목록 보기
17/18

공부한 것을 정리하는 용도의 글이므로 100% 정확하지 않을 수 있습니다.
참고용으로만 봐주시고, 내용이 부족하다고 느끼신다면 다른 글도 보시는 것이 좋습니다.
+ 틀린 부분, 수정해야 할 부분은 언제든지 피드백 주세요. 😊
                                            by. ryalya





Objective C를 사용했던 때는 메모리 관리를 어떻게 했을까?

MRC (Menual Reference Couting)

  • 힙에 메모리를 직접 할당/해제

  • Cocoa Framework 에서 레퍼런스 증가는
    alloc, new, copy, mutableCopy, retain 등을 사용, 레퍼런스 감소는 release 사용

  • Swift에서는 ARC만이 사용되기때문에 Objective-C를 사용하지 않는다면 자세히 이해할 필요는 없음.
    (다만 Objective-C 코드를 만져야한다면 알아야 함....)



ARC (Auto Reference Counting)


Swift uses Automatic Reference Counting (ARC) to track and manage your app’s memory usage. In most cases, this means that memory management “just works” in Swift, and you do not need to think about memory management yourself. ARC automatically frees up the memory used by class instances when those instances are no longer needed.

  • RC 자동 관리 방식 (WWDC 2011 발표)

  • iOS에서 메모리 관리하는 방법으로 자동으로 메모리를 관리해줌. (memory leak 방지)

  • 컴파일(build)할 때, 코드를 분석해서 자동으로 retain, release 코드를 생성해줌.

  • 메모리에 참조된 횟수를 추적해(계산해) 더 이상 참조되지 않는 인스턴스(참조 횟수가 0이 되면)를 deinit을 호출하여 메모리에서 해제해줌.

  • 힙(heap)에 할당된 인스턴스의 메모리를 자동으로 해제해줌.

  • retain cycle에는 유의해야 함.


* Heap : class, closure 등의 참조형(reference) 자료 들이 머무는 공간이자, 개발자가 동적으로 할당하는 메모리 공간.


Application은 Compile, Linking, Runtime (linking)을 거쳐 빌드되어 실행된다.


Reference Counting


인스턴스는 하나 이상의 소유자(Owner)가 있는 경우 메모리에 유지된다.

위의 ARC 특징에서 언급한 것 처럼 소유자가 없다면 그 즉시 메모리에서 제거된다.

제거 시점을 파악하기 위하여 소유자 수를 저장하는데 이것을 참조 카운트(Reference Count)라고한다.

참조 카운트가 1 이상이면 메모리에 유지되고, 0 이 되면 메모리에서 제거된다.

클래스 인스턴스를 변수에 저장하면 변수가 소유자가되고 이 때 참조 카운트는 1이 증가하는 방식으로 만약 또 다른 변수에 저장하면 2가 된다.

코드 레벨로 보자면 해당 인스턴스의 retain 메소드를 호출하는 것과 같음.

참조 방식에는 3가지가 있다.

  • Strong
  • Weak
  • Unowned

Strong

  • 강한 참조
  • default (선언할 때 따로 참조 방식을 지정하지 않으면 Strong으로 설정됨)
  • 인스턴스의 주소값이 변수에 할당될 때 RC가 증가 → 강한 참조(Strong)
  • 순환참조 유의
<간단 예시>

var person = Person() // RC 1 증가
person = nil // RC 1 감소, RC = 0 , 메모리 해제
<예시>

class Person {
    var name: String?
    var age: Int?
    
    init(name: String?, age: Int?) {
        self.name = name
        self.age = age
    }
   deinit {
        print("\(name) is being deinitialized")
    }
}
 
let circle = Person(name: "one", age: 25)
<예시2>

var person1: Person?
var person2: Person?

person1 = Person(name: "harry") // RC 1 증가 → 총 카운트 1
// Prints "harry is being initialized"

person2 = person1 // RC 1 증가 → 총 카운트 2


// nil = 소유권 포기, 즉시 강한 참조가 제거됨
person1 = nil // 참조카운트 1 감소 → 총 카운트 1
// 현재 RC = 1 이므로 인스턴스가 제거되지 않음.

person2 = nil // 참조카운트 1 감소 → 총 카운트 0
// Prints "\harry is being deinitialized"
// RC = 0. 인스턴스 해제

순환 참조

두 개의 객체가 서로가 서로를 참조하고 있는 형태

? 문제점 ?
→ strong으로 선언된 변수들이 순환참조 됐을 시, 서로가 서로를 참조하고 있어서 변수를 nil로 지정해도 RC가 0이 되지 못한다.

또한, 해당 인스턴스를 가리키던 변수도 nil이 지정됐기 때문에 인스턴스에 접근 할 수 있는 방법도 없어 메모리 해제도 할 수 없다.

→ Strong 참조로 인한 순환참조를 강한 순환 참조라고 함.

→ 즉, 어플이 죽기 전까지 memory leak이 계속 발생하게 됨.

→ 이런 순환 참조의 문제점을 해결하기 위해서 weak, unowned를 사용할 수 있다.



Weak

  • 약한 참조
  • RC를 증가시키지 않음.
  • 참조하던 인스턴스가 메모리에서 해제된 경우, 자동으로 nil이 할당되어 메모리가 해제된다. → weak : 무조건 옵셔널 타입의 변수
  • 순환 참조를 일으키는 프로퍼티 앞에 weak
  • 약한 순환 참조는 RC값을 올리지 않음.
  • 둘 중에 수명이 더 짧은 인스턴스를 가리키는 애를 약한 참조로 선언함.
<간단 예시>

weak var person = Person()  // 객체가 생성 되지만 weak이기 때문에 바로 객체가 해제되어 nil이 됨


Unowned

  • 미소유 참조
  • 강한 순환 참조를 해결할 수 있음
  • RC 값을 증가시키지 않음
  • 인스턴스를 참조하는 도중에 해당 인스턴스가 메모리에서 사라질 일이 없다고 확신하는 것이 특징.
    → 참조하던 인스턴스가 만약 메모리에서 해제된 경우, nil을 할당받지 못하고 해제된 메모리 주소값을 계속 들고 있음.
    → 선언된 변수가 가리키던 인스턴스가 메모리에서 먼저 해제된 경우, 접근하려 하면 에러를 발생시킴

→ 사라지지 않을 거라고 보장되는 객체에만 설정하는 것이 crash가 발생하는 것을 방지할 수 있음.

→ 안전성을 위해서는 weak 사용을 권장

but!

weak는 객체를 계속 추적하고 있음.
이렇게 계속 객체를 추적하고 있는 것도 런타임시 오버헤드가 될 수 있음.
따라서 확실한 객체에는 unowned를 사용하는 것이 좋음.






Reference

iOS) 메모리 관리 (1/3) - ARC(Automatic Reference Counting)

[Swift] ARC 뿌시기

[iOS] ARC(Automatic Reference Counting)

0개의 댓글