- Object Lifetime and ARC
- init() 부터 마지막 사용시까지
- 생명주기가 끝나면, 자동으로 메모리가 해제된다. → 참조 카운트가 0이 되는 시점
- ARC는 참조 카운팅 방식으로 메모리를 관리한다.
- Swift 컴파일러가 retain, release 명령을 자동으로 넣어준다.
- release는 객체가 마지막으로 참조되는 라인 바로 다음에 들어간다. → 이 부분이 다른 언어와 다르다. 다른 언어는 중괄호가 닫힐때 수행된다.
- init은 자동으로 참조 카운트를 1로 만든다.
- 객체가 다른 곳에 할당될 때, retain이 들어간다.
- 하지만 실제로는 ARC 최적화 등에 의해 실제 생명주기 끝보다 더 뒤쪽에서 해제될 수 있다.
- 보통은 언제 해제되던 상관은 없는데, weak나 unowned, deinit에서의 사이드 이펙트 수행 등에 의존하는 로직은 영향을 받을 수 있고 이는 버그의 원인이 된다.
- 지금은 동작한다고 하지만, 그건 그냥 우연일 뿐이다. 나중에 언제든 바뀔 수 있다.
- 이러한 버그들이 오랜시간 숨어 있다가 튀어나올 수 있다.
- Observable Object Lifetimes
- weak와 unowned
- 참조 카운팅에 참여하지 않는다.
- 순환 참조를 끊는데 주로 사용한다.
- 사용중에 해제되어 버릴 수도 있다.
- weak는 nil로 세팅된다.
- 강제 해제하면 크래시고, 옵셔널 바인딩하는 건 버그를 숨기는 행위다.
- unowned는 트랩에 걸리게 된다.
- weak와 unowned문제 해결 - 성능이냐, 유지보수냐
- withExtendedLifetime(): 명시적으로 해당 블록내까지 생명 주기가 연장됨을 보증한다.
- 빈 블록으로 호출해도 유효하고, defer에서 호출하는 방법도 있다.
- 다만 이 방법은 수동으로 매번 해줘야 한다는 단점이 있다. weak나 unowned가 쓰이는 곳마다.
- 잘 통제되지 않는다면 유지비용을 늘리는 원인이 된다.
- 강한 참조를 통해서만 접근하게 하기
- 구조 변경
- 사이클이 꼭 필요한가?
- 트리 구조로 바꿀 수는 없는건가?
- 성능적으로 약간 손해 볼 수도 있지만, 그래도 버그나는 건 막을 수 있다.
- 소멸자에서의 부수효과
- 소멸자는 해제 전에 호출된다.
- 버그의 원인이 될 수 있는 것
- 전역적으로 영향을 미치는 부수 효과
- 클래스의 디테일한 정보를 활용하는 부수효과
- 해결방법은 위와 같다.
- deinit 대신 defer를 활용하는 것을 고려해보자
- 새로운 기능
- XCode 13에 추가된 옵션: Optimize Object Lifetime
- 이전보다 더 일관적으로 사용이 끝나면 바로 해제가 되게 된다.