본문 바로가기
개발/iOS

[Swift] ARC, Retain Cycle, weak, unowned

by 마자용 2022. 2. 22.

들어가며

셀 안의 버튼을 클릭했을 때 클로저로 처리하고 싶어 방법을 찾아보던 중 발견한 코드들에 [unowned self] 키워드가 붙은 것을 보았고, 비슷한 키워드인 [weak self]도 생각나 궁금해져 공부해보고 정리하게 되었습니다 ...

 


ARC란 무엇인가?

Automatic Reference Counting의 줄임말입니다.

직역하자면 자동 + 참조 + 카운팅


자세한 정의는 이렇습니다.

 

Automatic Reference Counting — The Swift Programming Language (Swift 5.6)

Automatic 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 don’t need to think about memory management your

docs.swift.org

1. Swift에서의 메모리 관리 방식이며, 객체에 대한 참조 카운팅을 관리합니다.
- 카운팅이 0이 되면 자동으로 메모리를 해제시킴 (→ 이를 deinit이라고 함)

2. 컴파일 시간에 (= 빌드할 때) 실행되어 retain과 release를 적절한 위치에 삽입해줍니다.
- retain, release는 객체의 유지와 감소를 말함


컴퓨터에는 네 가지 메모리 영역이 존재하는데요! (Data / Heap / Stack / Code)
여기서 참조형 자료들 (대표적으로 class와 closure가 있죠?)은 Heap에 속하며 동적으로 할당됩니다.

동적으로 할당되면 시간적인 측면에서 효율성이 좋아지지만, 따로 관리를 해줘야 하는 번거로움이 생깁니다.

ARC는 Heap 영역 (= 메모리)에 참조되는 객체들을 counting 횟수에 따라 자동으로 관리 (= 할당 & 해제) 해줍니다.

 


Retain Cycle

하지만 할당과 해제가 제대로 되지 않는다면, 메모리 누수가 발생하게 됩니다.
(→ 앱에서 사용하는 메모리 용량이 늘어나고, 크러쉬 발생 가능성이 높아짐)

ARC가 제대로 동작하지 않아 도움을 주어야 하는 이러한 상황을 Retain Cycle이라고 하는데요,
여기에 대한 해결법 두 가지가 바로 weak reference, unowned reference인 것입니다!

 

1. weak reference (약한 참조)

  • 참조가 인스턴스를 방해하지 않겠다는 의미입니다.
  • 옵셔널 타입으로, 할당이 제거되면 (= 참조하고 있는 인스턴스가 메모리에서 해제가 되면) ARC는 nil로 참조값을 대체합니다.
  • 참조하고 있는 객체의 생명 주기가 짧은 경우에 사용합니다.

 

2. unowned reference (미소유 참조)

  • weak와 비슷하지만, 옵셔널이 될 수 없다는 점이 다릅니다. (= nil이 될 수 없음)
  • 때문에 사용 시 인스턴스가 메모리에서 해제된 이후 다시 접근하지 않을 확신이 있는 변수에만 붙여줍니다.

 


활용

[weak self], [unowned self]
클로저의 강한 순환 참조를 막기 위해 사용되는 캡쳐 리스트입니다.

 

캡쳐 리스트란?
- 클로저 안에서의 참조 타입을 정의하는 리스트

강한 순한 참조란?
- retain cycle을 발생시키는 중첩된 참조 구조
- 예시 뷰 컨트롤러가 컬렉션 뷰를, 컬렉션 뷰가 컬렉션 뷰 셀을, 컬렉션 뷰 셀이 클로저를 소유하는 상황

 

저는 셀 안의 버튼을 클릭했을 때 알랏창을 띄우기 위해 셀에 클로저 변수를 만든 다음 함수에 넣어두고, 뷰 컨트롤러의 cellForItemAt에서 불러와서 사용을 해줬는데요! 여기서 [unowned self]가 사용되었습니다.

cell.accept = { [unowned self] in
    let alert = UIAlertController(title: "지민지민님이 캘린더 공유를 요청했어요", message: "수락하면 상대방이 지안님의 캘린더를\n볼 수 있어요!", preferredStyle: .alert)
    let refuse = UIAlertAction(title: "취소", style: .default, handler: nil)
    let accept = UIAlertAction(title: "확인", style: .default, handler: nil)
    [refuse, accept].forEach {
        alert.addAction($0)
    }
    self.present(alert, animated: true)
}

[unowned self]나 [weak self]를 붙여주지 않고 그냥 [self]를 사용한다면, 클로저에서 컬렉션 뷰 셀, 컬렉션 뷰 셀에서 컬렉션 뷰, 컬렉션 뷰에서 뷰 컨트롤러로 타고 올라가야겠죠 ? 이러한 사이클을 방지해주겠다는 의미가 되는 것입니다.

 

 

지금까지의 내용을 한 마디로 정리하자면, 클로저에서 [weak self]와 [unowned self]를 붙여주는 이유는 retain cycle, 즉 메모리 누수를 막기 위해서다 ... 라고 할 수 있겠네요 ㅎㅎ

 




마무리 하겠습니다 ~!
혹시 잘못된 내용이 있다면 댓글로 꼭 알려주세요!

 

참고

- [Swift] weak, unowned, Retain cycle 톺아보기
- [Swift] ARC 뿌시기
- unowned self vs weak self (캡쳐 리스트)-(1)

댓글