IOS/Swift

[TIL] Swift. ARC ( Automatic Reference Counting )

 

1. What is ARC?

  • Automatic Reference Counting
  • 이름에서 알 수 있듯이 reference의 숫자를 자동으로 세는 메모리 관리자입니다.
  • 이 말은 객체의 메모리 할당시 인스턴스에 reference를 저장하는 객체만 ARC의 영향을 받는다는 말입니다. 따라서 Value Type은 관리 대상이 아닙니다. 대상: Class, Closure
  • Swift는 각각의 객체에 retain counts를 가지고 있고 이 안에 Strong reference count가 0이된 것을 ARC가 메모리에서 해제합니다

2. Resource Management

ViewController가 생성한 모든 뷰와 객체들은 ViewContoller의 책임 UIViewController의 LifeCycle에 따라 생성되었다가 자동 소멸되기도 하지만 ARC 개념에 맞게 관리 필요 메모리 부족시 didReceiveMemoryWarning 메서드에서 캐시메모리 등 꼭 유지하지 않아도 되는 메서모리들은 정리 필요

  • Automatic Reference Counting
  • 사용자는 메모리 관리를 신경쓰지 않아도 swift에서 자동으로 메모리를 관리합니다
  • 하지만 때때로 ARC는 사용자의 의견이 필요할 때가 있습니다
  1. how it works
    1. 매번 새로운 클래스 인스턴스를 만들때마다 ARC는 메모리에 조각을 할당합니다
    2. 이 메모리는 인스턴스의 타입을 가지고 있고, 값과 모든종류의 stored property값을 포함합니다.
    3. ARC가 이미 사용중인 인스턴스를 할당 해제할 경우 이 인스턴스에 접근하려고 하면 에러가 발생합니다
    4. 따라서 사용자는 필요한 인스턴스를 끊어지지 않도록 해야합니다
    5. ARC는 적어도 하나의 연결이 살아 있는한 할당을 해제하지 않습니다.
    6. 여기서 연결은 Property, Variable, Constant의 참조를 의미합니다

3. Strong Reference Cycle

class Dog {
    var cat: Cat?
    deinit {
        print("Remove dog")
    }
}
class Cat {
    var person: Person?
    deinit {
        print("Remove cat")
    }
}

// 1
var dog: Dog? = Dog() // 인스턴스 생성 후 다음 값 출력시 CFGetRetainCount(dog) -> 2 출력(기본 count)
var cat: House? = Cat()

// 2
dog?.cat = cat
cat?.dog = dog

// 3
dog = nil
cat = nil
  • 위의 코드는 강한 상호참조를 보여주는 사례입니다.
  • dog와 cat을 모두 nil로 처리를 했음에도 불구하고 deinit에 있는 print코드가 출력되지 않았습니다.
    • dog과 cat 인스턴스를 각각 생성하였고, 각각의 인스턴스는 strong reference count가 1이 됩니다.
    • cat.dog와 dog.cat에 각각 cat와 dog을 할당하였기 때문에 두 인스턴스의 strong reference count는 2가 되었습니다.
    • dog과 cat 인스턴스를 nil처리 했기 때문에 두 인스턴스의 strong reference count는 1이 됩니다. 그리고 strong reference count가 0이 되지 않았기 때문에 메모리 해제는 일어나지 않습니다.
    • 이와 같은 케이스에서 가장큰 문제는 인스턴스의 메모리가 해제되지 않았는데 이에 접근할 방법이 없다는 것 입니다. 즉 dog.cat은 메모리에 남아있는데 dog자체가 없어져버려서 접근자체가 불가능합니다.
    • 이러한 상황을 메모리 누수 (Memory Leak)가 발생했다고 하고 반드시 고쳐야합니다.

4. Strong Reference Cycle 없애기

  • 강한 상호 참조를 없애는 핵심 원리는 strong reference count를 세지 않는 것입니다. 여기서 중요한 것은 reference count를 아예 세지 않는 것이 아니라, strong reference count를 세지 않는 것입니다. 즉, weak, 혹은 unowned를 사용하여 property를 선언하게 되면 해당 인스턴스에 대해서는 strong reference count를 카운팅하지 않고, weak reference count를 카운팅합니다. 이를 Swift 공식 문서에서는 weak reference, unowned referece가 property에 대해 strong hold를 하지 않는다고 표현합니다.
  • 객체에 포함된 retain counts 안에 strong reference count가 있고 부수적으로 weak, unowned count 가 있는데 만약 strong reference count가 0이 된상태에서 부수적인 카운트가 1이라해도 ARC는 Strong count를 기반으로 관리하기 때문에 메모리에서 해제됩니다.
따라서 위의 예제에서 클래스에서 선언된 각각의 변수를
class dog {
	weak var cat: Cat?
	deinit {
		print("remove Dog")
}
class cat {
	weak var cat: Dog?
	deinit {
		print("remove Cat")
}
이런식으로 선언해주면 nil값으로 바뀌는 순간 메모리에서 해제가 됩니다.

최종적으로 remove Dog, remove Cat이 출력되는것을 볼수있습니다.
  • 이러한 weak 가 사용된 대표적인 예제는 IBOutlet입니다
    • ViewController에서 storyboard를 통해 SubView를 생성하여 IBOutlet을 생성할 경우 다음과 같은 형태로 property가 설정됩니다.

class ViewController: UIViewController {
    @IBOutlet weak var myView: UIView!
}
// 많이 익숙한 코드죠?? ㅎㅎ
  •   이렇게 사용되는 이유는 UIViewController 와 UIView모두 Class로 작성된 reference type의 데이터이기 때문에 Strong count에 의해 메모리 누수가 발생할 수 있기 때문입니다

     

5. Unowned & Weak

  • 언제 두가지를 사용해야 하는지는 설명했지만 어느때에 두개중 하나를 써야하는지는 이야기 하지 않았습니다.
  • 간단히 이야기해서 두가지다 기능은 똑같지만 참조하는 클래스와의 관계에 따라서 표현이 틀려집니다
  • 사람과 아파트의 관계에서 아파트안에 사람이 들어가서 살수도 안살수도 있습니다. 아파트는 사람이 없어도 그대로 존재할수 있고 따라서 있든 없든 상관없습니다 = Weak를 사용합니다.
  • 마찬가지로 사람과 신용카드의 관계에서 신용카드는 사람의 명의 없이는 만들어질수 없습니다. 이렇게 신용카드는 사람없이는 만들어질수 없으므로 Unowned를 사용합니다.
  • 간단하게 이야기해서 관계가 같이 존재해야하는 것 Unowned , 반대의 경우 Weak를 사용하면 됩니다.

'IOS > Swift' 카테고리의 다른 글

[TIL] Swift. awakeFromNib  (0) 2020.10.14
[TIL] Swift. ViewController LifeCycle  (0) 2020.10.12
[TIL] Swift. dequeueReusableCell  (0) 2020.10.09
[TIL] Swift. Class 2편  (0) 2020.10.07
[TIL] Swift. Class 1편  (0) 2020.10.07