제목: Catching Leaky View Controllers Without Instruments

레테인 사이클(retain cycles)에의해 생긴 메모리 누수를 찾아내는 방법 중 잘 알려진 방법은, 더이상 화면에 있지 않을때 모든 뷰컨트롤러들이 디얼록되었는지 확인하는 것이다. 이 과정은 각각이 할당해제되기 전에 직접 반복해서 시행해보아야하는데, 재밌지도 않을 뿐더러 여기서 에러를 만들어내기도 한다. 만약 좀 더 일찍 UIViewController 누수에대한 
과정을 알 수 있었다면, 매일 개발하는동안 훨씬 낫지 않겠는가?

UIViewController의 잘 알려지지 않은 두개 프로퍼티에대해 감사하게 될 수도 있다.
  • isBeingDismissed: 모달로 나타난 뷰컨트롤러가 dismiss될 때, 이 프로퍼티는 true이다.
  • isMovingFromParentViewController: 부모 뷰컨트롤러로부터 이 뷰컨트롤러가 제거될 때, true가 된다. 이것은 UINavigationController 스택에서 뷰컨트롤러가 pop될 때와 같은, 시스템 컨테이너에서 제거되는 것도 포함된다.
이 프로퍼티 중 하나가 true면, 곧 뷰컨트롤러가 디얼록될 수 있다는 것을 알 수 있다. 정확히 얼마나 있어야 내부적으로 정리된 상태를 만들고 ARC가 디얼록할지는 모른다. 간단하게 생각해서 2초보다 더 길지는 않을 것이라 가정하자.

우리가 생각한 것을 코드로 만들어보자.
extension UIViewController {
   public func dch_checkDeallocation(afterDelay delay: TimeInterval = 2.0) {
       let rootParentViewController = dch_rootParentViewController

       // We don’t check isBeingDismissed simply on this view controller because it’s common

       // to wrap a view controller in another view controller (e.g. in UINavigationController)

       // and present the wrapping view controller instead.

       if isMovingFromParentViewController || rootParentViewController.isBeingDismissed {
           let type = type(of: self)
           let disappearanceSource: String = isMovingFromParentViewController ? "removed from its parent" : "dismissed"

           DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: { [weak self] in

               assert(self == nil, "\(type) not deallocated after being \(disappearanceSource)")
           })
       }
   }

   private var dch_rootParentViewController: UIViewController {
       var root = self

       while let parent = root.parent {
           root = parent
       }

       return root
   }
}
흥미로운 부분은 asyncAfter(deadline:execute:) 호출에서 일어난다. 먼저 sefl를 weak로 만들면([weak self]), 나중에 호출할 클로저에의해 리테인되지 않는다. 그리고 self가 nil이면 assert를 건다(여기서 self는 UIViewController). 뷰컨트롤러가 살아있게 잡아두는 리테인 사이클을 가질때만 nil이 아니다.

이제 마지막으로 해야할 일은 모든 뷰컨트롤러의 viewDidDisappear(_:)에서 dch_checkDeallocation()을 호출하는 것이다(부모로부터 제거되거나 dismiss된 후에도 살아있도록 잡아두는 것을 제외하고)
override func viewDidDisappear(_ animated: Bool) {
   super.viewDidDisappear(animated)

   dch_checkDeallocation()
}
만약 누수가 일어나고 있으면, assert에서 실패나 나올 것이다(-Onone 빌드에서만 가능).

여기서 우리는 그냥 메모리 그래프 디버거(Memory Graph Debugger)를 열어 사이클의 원인을 찾고 고치면 된다.

이 방법을 통해 새로 소개된 리테인 사이클을 빠르게 배우는데 도움이 될거라 생각한다. 여러분도 이 방법이 즐거웠길 바란다! production-ready 코드(더 많은 주석과 if DEBUG 확인으로)는 GitHub의 DeallocationChecker에서 확인해볼 수 있다.



이 블로그는 공부하고 공유하는 목적으로 운영되고 있습니다. 번역글에대한 피드백은 언제나 환영이며, 좋은글 추천도 함께 받고 있습니다. 피드백은 

으로 보내주시면 됩니다.



WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

트랙백  0 , 댓글  0개가 달렸습니다.
secret
제목: iOS — Identifying Memory Leaks using the Xcode Memory Graph Debugger

이 짧은 포스팅에서 내가 설명할 것은 아래와 같다
  • Xcode의 메모리 그래프 디버거란
  • 이것을 어떻게 사용하고, 몇가지 팁들
  • 장/단점들

이것이 무엇인가
짧게말해 메모리 그래프 디버거는 다음 질문의 답변에 도움을 준다. 한 오브젝트가 왜 메모리에 남아있는가?

Xcode의 메모리 그래프 디버거는 레티인 사이클과 메모리 누수를 찾아내고 고치는데 도움을 준다. 그 일이 발생하면, 앱 실행이 일시정지되고 현재 힙에서 오브젝트를 보여주는데, 이 오브젝트가 살아있게 하는 참조들이 무엇인지 그 관계와 함께 나타난다.


어떻게 사용하는가
리테인 사이클과 메모리 누수를 식별하는데 3가지 간략한 단계가 있다.
  • 1. 아래처럼 Xcode scheme editor로 stack logging에 체크한다.

live allocations을 위해 Malloc stack logging을 켜기live allocations을 위해 Malloc stack logging을 켜기


'Live Allocation'의 logging만 켰다. 이것은 디버깅할때 'All Allocations'를 선택하는것보다 오버헤드가 적고, 레테인 사이클과 누수를 식별하는데 필요한 것이다.
  • 2. 분석하고 싶은 앱을 실행시키고(리테인 사이클이나 누수가 의심되는 행동), 그 디버그 바 버튼을 선택하여 메모리 그래프 디버깅 모드로 들어가자.

메모리 그래프 디버깅 버튼메모리 그래프 디버깅 버튼

  • 3 .메모리 그래프 디버거는 앱 실행을 일시정지하고 아래를 보여준다.

Xcode의 메모리 그래프 디버거 모드Xcode의 메모리 그래프 디버거 모드

왼편에 디버그 네비게이터가 앱의 힙 내용을 보여준다.

디버그 네비게이터에 type/instance를 선택하면 가운데 패널에서 인스턴스 참조들을 보여준다.

가운데 참조 패널에 인스턴스를 선택하면 오른편의 인스팩터 페널에서 일반적인 오브젝트 메모리 정보와 allocation backtrace를 보여준다.

누수는 아래처럼 디버그 네비게이터에서 볼 수 있다.

디버그 네비게이터에 나타난 누수디버그 네비게이터에 나타난 누수


  • 1. 메모리 누수를 식별하는데 도움이 되기위해서, 아래처럼 누수만 보이도록 힙 내용을 필터링할 수 있다.

메모리 누수를위한 필터링메모리 누수를위한 필터링

  • 2. 런타임 이슈 네비게이터도 유용한데, 식별된 모든 누수의 숫자를 표시한다.

수많은 메모리 누수들!수많은 메모리 누수들!


좋은점과 나쁜점
  • 좋은점: 운좋게 누수를 쉽게 찾아낼 수 있다(간단한 리테인 사이클). 예를들어 한 오브젝트가 클로저 프로퍼티 안에서 자신을 붙잡고 있을때. 이건 붙잡고 있는 참조를 weak로하여 쉽게 고쳐진다.
  • 나쁜점: 잘못 알려주는 경우. 예를들어 UIButton 오브젝트를 만들고 UIToolBars 아이템 배열에 추가했는데, 메모리 누수로 나왔지만 그 이유는 볼 수가 없었다.


유용한 링크들

이게다다! 📱🚀👍🏽


이 블로그는 공부하고 공유하는 목적으로 운영되고 있습니다. 번역글에대한 피드백은 언제나 환영이며, 좋은글 추천도 함께 받고 있습니다. 피드백은 

으로 보내주시면 됩니다.



WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

트랙백  0 , 댓글  0개가 달렸습니다.
secret