iOS 개발일지
0905 새싹 45회차 정리 - ARC 본문
iOS 앱 개발자 데뷔 과정 45회차
#1. Checklist
- ARC (Automatic Reference Count)
메모리 영역 중 Heap은 클래스의 인스턴스, 클로저 등을 저장한다.
Heap에 저장되는 메모리는 직접 할당하고 해제할 수 있다.
ARC는 메모리 메모리를 추적하고 관리하는 시스템으로, 필요하지 않은 인스턴스가 있다면 자동으로 메모리에서 해제한다.
ARC는 인스턴스를 참조하는 횟수만큼 RC (Reference Count) 를 증가시키고, 인스턴스가 해제될 때 RC를 감소시킨다.
더 이상 인스턴스를 참조하고 있는 변수, 상수 등이 없는 경우 ARC는 해당 인스턴스가 필요 없다고 판단하고 자동으로 메모리에서 해제한다.
대부분의 경우에 대해서는 ARC가 메모리를 알아서 관리해주지만, 인스턴스가 서로를 참조하는 상황에 대해서는 Strong Reference Cycle (강한 순환 참조) 가 발생할 수 있어, Memory Leak (메모리 누수) 가 발생하지 않도록 해주어야 한다.
참고) Objective-c는 ARC가 등장하기 전까지 MRC (Manual Reference Counting) 로 수동 메모리 관리를 해왔다.
일반적으로는 인스턴스가 생성될 때 참조 횟수가 늘어나고, nil을 통해 해제하게 될 경우 메모리에서 참조 횟수가 0이 되므로 deinit이 호출된다.
- weak
weak로 선언하게 되면 인스턴스 참조가 발생하더라도 RC를 +1 하지 않는, 약한 참조가 된다.
weak로 선언할 경우, 런타임에서 nil로 값이 변경될 가능성이 있기 때문에 항상 옵셔널 타입의 변수로 선언한다.
weak를 사용할 때는 메모리를 해제하는 순서가 영향을 미친다.
순환 참조하고 있는 두 인스턴스가 동시에 메모리에서 해제된다면 크게 상관이 없지만, 특정 인스턴스에 대한 참조만 해제될 경우에는 경우에 따라 메모리에서 해제되지 않을 가능성도 있다.
" 수명이 더 짧은 인스턴스를 가리키는 쪽을 약한 참조로 선언해야 한다 " 라는 말은 이런 뜻이다.
그냥 양쪽 다 써버리는 것이 가장 빠르고 쉽지만, 그렇더라도 알고 쓰자!
- unowned
weak와 동일하게, 인스턴스를 참조할 경우 RC를 +1 하지 않는 특성을 가지고 있다.
차이점은, 참조하고 있는 인스턴스가 메모리에서 해제되더라도 자동으로 참조된 인스턴스의 메모리를 nil로 변경시키지 않는다.
메모리에서 인스턴스가 사라져도, 인스턴스를 참조하고 있는 변수에서는 메모리 주소값을 보유하고 있게 된다.
따라서 인스턴스의 수명이 다른 인스턴스와 동일하거나 더 긴 경우에 unowned를 사용한다.
만약 인스턴스 (user) 를 메모리에서 제거하고 guild.owner를 호출할 경우, 인스턴스가 존재하지 않아도 인스턴스의 메모리 주소는 남아있기 때문에, 해당 위치에 대한 값을 찾기 못해 위와 같은 에러가 발생한다.
- Delegate
- weak var delegate
weak로 delegate를 선언하지 않을 경우 RC에 의해 메모리에서 인스턴스가 제거되지 않는다.
따라서 약한 참조 관계를 만들어주기 위해, delegate 패턴을 사용할 때는 weak를 꼭! 선언해야 한다.
하지만 만약 weak를 선언했는데 위와 같이 오류가 난다면, 아래의 AnyObject를 통해 해결이 가능하다.
- AnyObject
weak로 선언되는 것은 참조가 가능할 때 뿐이고, 참조가 가능하다는 것은 프로토콜에 대한 클래스 제약이 설정되어야 한다.
따라서 MyDelegate protocol에 클래스 제약인 AnyObject를 설정해야 한다.
- Closure
- Closure Capture
클로저 또는 중첩 함수 외부에 선언된 변수를 클로저 또는 중접 함수에서 사용하게 될 경우, 외부 변수를 내부적으로 저장해서 사용하게 되는데, 이를 캡쳐되었다고 표현한다.
클로저는 값을 캡쳐할 때 캡쳐할 값이 값 타입인지, 참조 타입인지와 관계 없이 항상 Reference Capture를 하게 된다.
Reference Capture이기 때문에 참조 관계가 되고, 참조이기 때문에 클로저 내부의 값과 외부의 값이 모두 변경된다.
이는 클로저 내부에서 외부에 존재하는 값을 변경하더라도 값이 동일하게 적용된다.
따라서 참조 관계에서 벗어나기 위해서는 값 타입으로의 캡쳐가 필요하다.
Closure를 선언할 시점에 대한 값을 값 타입으로 캡쳐할 수 있고, 캡쳐하기 위해서는 [number]와 같은 형태로 명시할 수 있으며, 이러한 형태를 Capture List 라고 한다.
값 타입으로 캡쳐가 될 경우, 상수 형태로 캡쳐되기 때문에 클로저 내부에서 값에 대한 변경은 불가능하다.
- unowned self 와 weak self
클로저 안에서 self를 사용할 때에도 강한 순환 참조가 일어날 수 있어, 캡쳐를 통해 방지할 수 있다.
unowned와 weak는 위에서 설명한 것과 거의 동일하게 작동한다.
- weak self 에서의 guard vs self?
[weak self] 를 사용하는 경우 self가 옵셔널 타입이기 때문에 옵셔널 바인딩 혹은 체이닝을 거쳐 사용할 것이다.
만약 옵셔널 바인딩을 사용하는 경우 self가 nil이 아닐 경우, RC를 올리는 self를 생성한다.
이후로는 클로저가 종료될 때까지 self가 살아있을 수 있고, 그때까지 self의 해제를 지연시킨다.
반면 옵셔널 체이닝을 사용하는 경우 self? 가 사용될 때마다 nil인지 체크하고, nil일 경우에는 해당 코드를 실행하지 않는다.
따라서 self의 메모리 해제를 지연시키지 않고, 불필요한 작업을 하지 않을 수 있다.
#2. Assignment
- ARC 문서 예제 학습
https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html 참고
- Closure 문서 예제 학습
https://docs.swift.org/swift-book/LanguageGuide/Closures.html 참고
'SeSAC iOS 데뷔과정 2기' 카테고리의 다른 글
1011 새싹 67회차 정리 - Firebase (1) | 2022.10.11 |
---|---|
0906 새싹 46회차 정리 - Localization (0) | 2022.09.06 |
0904 9주차 마무리 - 2차 평가과제 (0) | 2022.09.05 |
0902 새싹 44회차 정리 - GCD (0) | 2022.09.02 |
0901 새싹 43회차 정리 - MVVM 맛보기2 (0) | 2022.09.01 |