- 📚 Swift Documentations - ARC
- 🚀 https://velog.io/@parkgyurim/Swift-ARC
Swift 는 ARC (Automatic Reference Counting) 라는 방식으로 메모리 관리를 하고 있습니다.
개발자가 직접 메모리 할당과 해제를 하지 않고 자동으로 해주기 때문에 편리함도 있지만, ARC 로 부터 발생할 수 있는 문제점 또한 존재합니다.
바로 강한 순환 참조 (Strong Reference Cycle) 인데요,
오늘은 강한 순환 참조가 무엇인지, 그리고 이를 해결할 수 있는 방법이 무엇인지 알아보겠습니다.
강한 순환 참조는 여러 인스턴스가 서로 강한 참조를 하는 사이클을 생성하는 것을 의미합니다.
강한 순환 참조가 문제가 되는 이유는 Swift 에서는 ARC 를 이용해서 Reference Count 가 0 이 되면 메모리에서 할당 해제를 하고 있는데, 강한 순환 참조가 생기게 되면 Reference Count 가 0 이 되지 않아 실제로 사용을 하지 않는 인스턴스가 메모리를 차지하고 있는 메모리 누수 현상이 발생하기 때문입니다!
그런데 강한 참조가 무엇일까요?
Swift 에서 Reference Type 의 인스턴스를 참조하는 방식은 3가지가 있습니다.
strong
referenceweak
referencenil
이 할당된다.Reference Count 를 증가시키지 않아 강한 순환 참조를 해결할 수 있는 하나의 방법이 될 수 있습니다.
nil
이 할당 될 수 있기때문에 옵셔널 타입으로 선언하여야 합니다.
📌 NOTE
ARC 가
weak
변수를nil
로 만들때는 프로퍼티 옵저버 (Property observer) 가 호출되지 않습니다.
unowned
referenceunowned
역시 Reference Count 를 증가시키지 않아 강한 순환 참조를 해결할 수 있는 하나의 방법이 될 수 있습니다.
하지만 weak
와 다르게 옵셔널 타입으로 선언하지 않아도 됩니다.
그 이유는 unowned
참조를 하면 참조하는 인스턴스가 메모리에서 할당 해제되어도 여전히 Heap 영역에 인스턴스가 있던 곳을 가리키고 있습니다. 즉 허상 포인터 (Dangling Pointer) 가 됩니다.
그렇기 때문에, 참조하는 인스턴스가 먼저 메모리에서 해제될 가능성이 없는 경우에만 사용해야합니다!
weak
vs unowned
Use a weak reference when the other instance has a shorter lifetime—that is, when the other instance can be deallocated first. In contrast, use an unowned reference when the other instance has the same lifetime or a longer lifetime. - 📚 Swift Documentations - ARC
weak
와 unowned
의 차이점은 참조하는 인스턴스가 메모리에서 할당 해제될 경우 에 볼 수 있습니다.
nil
이 됨dangling pointer
가 됨➡️ 그렇기 때문에 Swift 공식문서에서는
하는 것을 권장하고 있습니다.
"Dangling Pointer", "nil
이 될 수 있다" 의 관점에서 읽어보시면 이해하기 쉽습니다.
서로 다른 클래스 인스턴스간에 순환 참조가 있을때, 해당 변수를 weak
또는 unowned
로 선언해서 강한 순환 참조가 생기는 것을 막을 수 있습니다.
주의 사항으로는
weak
를 사용하는 경우 옵셔널 타입으로 선언unowned
를 사용하는 경우, 인스턴스간 lifetime을 고려class A {
weak var instance : B?
...
}
class B {
weak var instance : A?
...
}
클로저의 특성 중 Context Capture 라는 특성을 알고 계신가요?
Context Capture는 사용할 변수 등 클로저가 실행되는 Context를 캡쳐하는데, 이때 캡쳐는 값을 복사하는 것이 아니라 Reference Capture 를 하게 됩니다. (해당 변수가 Value Type 인지 Reference Type 인지 상관없이 Reference Capture 를 하게 됩니다.)
클로저가 실행되면
이렇게 클래스 내부에서 클래스 내부를 참조하는 클로저가 실행되면 강한 순환 참조를 일으킬 수 있습니다.
[weak self]
를 본 적 있으신가요?
이는 self
, 즉 (클로저가 속한) 클래스를 약하게 참조하겠다 라는 의미입니다.
클로저의 파라미터와 리턴 타입 앞에 캡쳐 목록을 지정할 수 있고, weak
, unowned
키워드로 참조 방식을 지정할 수 있습니다.
// 📌 클로저의 파라미터와 리턴값이 있는 경우
var someClosure1 = { [unowned self, weak delegate = self.delegate] (index: Int, stringToProcess: String) -> String in
...
}
// 📌 클로저의 파라미터와 리턴값이 없는 경우
var someClosure2 = { [unowned self, weak delegate = self.delegate] in
...
}
클로저에서도 마찬가지로
weak
를 사용할 경우 옵셔널 타입으로nil
을 가질 수 있음을 고려해야함unowned
를 사용할 경우 인스턴스간 lifetime을 고려해야함
오늘은 ARC 에 이어서 강한 순환 참조에 대해서 알아보았습니다.
틀린 정보 또는 궁금한 점이 있다면 댓글 부탁드립니다! 읽어주셔서 감사합니다‼️