원문 : https://medium.com/@kazmiekr/what-every-ios-developer-should-be-doing-with-instruments-d1661eeaf64f
Introduction
iOS 프로젝트를 마무리할때, 디바이스에 올려 테스트하여 크래쉬가 없이 잘 돌아가면 만족하고 끝낸적이 있을것이다. 그게 과연 올바른 마무리일까? Instruments를 사용하여 프로파일링(profiling)을 하지 않고 끝냈다면 잘 마무리 했다고 할 수 없다고 생각한다. 그 이유는 개발자 디바이스에서 잘 동작한다해서 실유저에서 크래쉬가 안난다는 보장은 없기 때문이다.
Xcode에는 Instruments라는 퍼포먼스 튜닝 어플이 포함 되 있다. 이 프로그램은 다양한 기준으로 개발자의 앱을 프로파일 할 수 있게 해준다. Instruments는 CPU 사용량, 메모리 사용량, 메모리 누수, 파일/네트워크 활성, 베터리 사용량 등을 측정할 수 있는 툴을 포함한다. 이것들은 Xcode에서 바로 시작할 수 있어서 쉽게 켤 수 있다. 그러나 지금 보고있는 프로파일 자료가 뭘 프로파일링 한건지 잘 모를수도 있으며 이런 상황은 개발자들이 이 깊은 잠재력을 가진 툴 사용을 저해할 수 있다.
이 많은 프로파일 툴 중에 어떤걸 선택해야할까? 먼저, 느려진 네트워크 요청이나 버벅거리는 스크롤과 같은 눈어띄는 퍼포먼스 이슈 해결을 위한 선택지들이 있을 수 있다. 그러나 나는 당신이 생각하기에 모든것이 정상적으로 돌아가는것으로 보이는 부분에서 CPU나 메모리 사용량을 체크해보는걸 추천한다.
프로파일을 할 때
이 이야기를 하기 전에 한가지 우리가 염두할 것은, 모든 개발자들이 항상 그들의 앱에 프로파일을 하지는 못한다는 것이다. 대부분은 데드라인과 예상 결과물이 존재한다. 어떨땐 앱을 프로파일링이고뭐고 마감을 빠듯하게 지키는것도 힘들 때가 있지만, 우리는 이런 상황까지 고려하여 '어느 시점에 프로파일링을 하면 좋을까'에 대해 얘기해보고자한다.
최소한 만든 앱을 앱스토어에 제출하기 전에 한번은 프로파일링을 해줘야한다. 당신은 앱스토어의 심사를 통과 한 후 유저들이 앱을 사용하면서 안 좋은 일들이 일어나길 바라지는 않을것이다. 프로파일링을 하지 않았다가 문제가 생긴다면, 좋지 않은 리뷰들이 많아질 것이고 다운로드 수는 감소할 것이다.
나는 당신이 주요 기능들을 끝낸 후에, 재빨리 프로파일을하여 모든것이 정상적으로 동작하는지 확인해보는 것을 제안한다. 만일 재빨리 하지 못하면, 잠재적인 이슈들이 점점 드러날테고, 그것이 한참 쌓이면 완전히 새로운 기획으로 바뀔 수도 있으며 이것은 앱 런칭을 심각하게 미루게 될것이다. 팀으로써 당신은 프로파일하는 시간을 개발 플랜의 일부로 살짝 넣도록 해야한다.
익숙하지 않은 새 프레임워크를 쓰게되면 Instruments를 켜는걸 제안한다. 또한 대부분의 iOS 프레임워크와 라이브러리는 끊임없이 바뀌며, 이런 변화는 당신이 익축하지 않은 프레임워크와 작업하는것과 비슷하다. 대부분 개발자들은 바뀌는게 언제인지 느낌이 올때가 있고, 만약 당신이 그런 느낌을 받았다면 얼른 당신이 만든 작업물들에서도 안정적으로 동작하는지 확인하기위해 프로파일링을 돌려야한다. 당신이 추가한 라이브러리에서 당신의 통제를 범위를 벗어나 발생될 수 있는 메모리 이슈가 있는지 확인하기 위한 프로파일에는 3rd party 라이브러리를 가져다 쓰는것 또한 좋은 방법이다.
Xcdoe에서 프로파일링
Xcode는 이전엔 Instruments 안에 묻어둔 많은 정보를 ‘Debug Navigator(Xcode 기능의 일부)’에 포함하기위해 위해 Instruments 밖으로 확장해왔다. 만일 당신이 ⌘6 단축키를 누르면, 앱에 대한 퍼포먼스 정보를 볼 수 있다. 여기서 CPU/Memory/Energy/Disk/Network 엑티비티의 빠른 요약 정보를 볼 수 있고 즉각적인 이슈를 볼 수도 있다. 여기서 상단에 보이는 ‘Profile in Instruments’ 버튼을 눌러서 Instruments를 실행할 수도 있는데, Instruments를 누르면 디버그 세션으로 이동할지 아니면 새것을 새로 시작할지 물어볼 것이다.
CPU 프로파일링
제일 먼저 우리가 프로파일해볼 것은 CPU 사용량이다. CPU 프로파일링을 시작하기위해 우리는 ‘Profile’ 이라는 프러덕트를 선택하고 타겟은 디바이스 선택해야한다. CPU 프로파일링을 할때, CPU 사용량에 대한 정확한 정확한 정보를 얻기 위해 실제 디바이스를 사용하기를 원할것이다. 만약 시뮬레이터로 타겟을 정하면 실제 디바이스 환경과는 꽤 많이 다른 머신의 CPU 정보를 얻게된다. 이상적인 테스트로는 당신의 작업물이 가장 느린 디바이스에서 잘 동작하는지 확인한 뒤에 빠른 환경에서 테스트 하는 것일 것이다.
CPU 프로파일링은 인터벌을 줘서 프로세스들이 동작하는 샘플을 얻어냄으로써 측정한다. 디폴트로 샘플은 1ms 단위로 얻어지지만, 원하면 바꿀 수도 있다. 스냅샷들 사이에서 어떤 프로세스들이 아직 돌고있는지 봄으로써, 그것들이 얼마나 길게 작동되고있는지 측정할 수 있다.
프로파일 빌드가 끝이나면, Instruments가 켜지면서 어떤 템플릿으로 프로파일링을 할지 물어본다. 우리는 CPU 사용량 측정을 위해 “Time Profiler”를 쓸 것이다.
이것은 Timer Profiler 셋업을 위한 초기 Instruments 화면이다. 이제 실제로 프로파일링을 하기위해 녹화 버튼을 누른다. 가끔 몇몇 이유로 녹화 버튼이 비활성되있는 경우가 있다. 그럴때는 오른쪽 상단에 비활성된 이유가 나타나 있을 것이다. 나는 ‘device is offline’이라는 상태에서 멈춘적이 있는데, 보통 디바이스를 재부팅하면 해결된다. 이제 녹화 버튼을 누르면 당신 앱에서 무슨 일이 일어나는지 정보를 보여주기 시작한다.
상단에는 녹화가 진행되는동안 시간이 지남에 따라 당신의 CPU 사용량 그래프가 보이며 하단에는 동작하는 동안의 프로세스들의 Call Tree가 보일것이다. 프로세스의 초기 덤프(dump)는 Call Tree에서 상단에 보이는 자료들은 매우 쓸때없이 많아 보인다. 또한 이것은 모든 시스템 라이브러리의 활성 상태를 보여줘버린다. 당신은 아마 당신이 작성한 코드에만 집중하고 싶을것이다. 다행히 중요한 정보만 빨리 찾도록 쉽게 설정하는 방법이 있다.
오른쪽에 톱니모양처럼생긴 ‘Display Settings’(⌘2)를 누르면 ‘Call Tree’를 위한 옵션들이 나온다. 디폴트로는 대부분 옵션이 오프되 있을 것이다. 우리는 우리가 원하는 것만 켜면 된다. 이제 이 옵션들이 뭘 하는 놈들인지 보자.
- Seperate by Thread — 많이 사용된 스레드 순서로 프로세스를 보여준다.
- Invert Call Tree — 스택을 뒤집어서 보여준다. 가끔 유용하게 쓰인다.
- Hide System Libraries — 시스템 라이브러리 프로세스는 숨기고 당신의 코드만 보여준다.
- Flatten Recursion — 하나의 개체에서 재귀로 호출된 콜들을 보여준다.
- Top Functions — 함수 호출이 그 함수로부터 불려진 함수에 의해 추가적인 시간이 쓰이는지 시간순으로 보여준다. 이 기능은 무거운 메소드가 어떤건지 찾는데 도움을 준다.
필자는 프로파일링할때 대체로 모두 체크하고 정보를 얻는데, 이게 꽤 유용하다. 필터링 옵션선택한 뒤 Call Tree를 보면, 당신의 앱에 메소드가 CPU를 얼마나 사용하는지 쉽게 확인할 수 있다.
이제 CPU 입장에서 무거운 메소드들 리스트를 모니터링 하면서 앱의 정확한 지점을 최적화 시켜볼 수 있다. 몇몇은 건드리기 힘들 수도 있지만 최적화 가능한 것들도 많이 있을 것이다. 필자는 앱 최적화를 어떻게 하는지에대해서는 깊게 보지는 않을 것이지만 여기 몇몇 생각해볼만한 것들이 있다:
- 무거운 작업은 UI 프로세스가 아닌 것은 다른 쓰레드로 옮긴다.
- 항상 다시불러올 필요가 없는 이미지, 데이터 등등의 것들은 캐싱한다.
- 당신은 꼭 필요하지 않는 UI 업데이트를 할 지도 모른다. UI 업데이트를 줄인다.
한가지 팁은 ‘Call Tree’ 섹션의 오른편에 존재하는 ‘Extended Detail’ 섹션이다. 이것은 당신이 선택한 스택 흔적(trace)를 볼 수 있고 이 라인을 더블클릭하면 정확히 당신 코드를 찾아 보여줄 것이다. 작은 Xcode아이콘을 클릭하면 그 동작중인 메소드를 Xcode에서 보여줄 것이다.
퍼포먼스 이슈를 해결하여 업데이트를 시킨 후에는 같은 방식으로 profiler를 돌리면서 이전 퍼포먼스보다 더 나아졌는지 체크한다. 이 과정을 당신이 만족할 때 까지 반복하면 된다.
메모리 프로파일링
다음으로 프로파일링 할 것은 메모리 프로파일링이다. 이것은 직접적인 이슈가 아닐때가 많아서 iOS 개발에서는 그냥 지나쳐버리기 쉬운 것중 하나이다. 만일 당신 앱에 메모리 누수가 발생하고 있는데 유저가 지속적으로 사용한다면 메모리는 점점 꽉 찬 뒤, 결국 아웃 오브 메모리 상황이 발생하여 앱이 꺼지게 될 것이다. 이런 상황은 당신의 앱한테만 안 좋은게 아니라 유저의 디바이스 입장에서 메모리 부족으로 다른 앱들에게도 안 좋은 영향을 끼치게 될것이다. 그럼 이제 Instruments를 이용하여 어떻게 메모리 누수를 방지를 할 수 있는지, 그런 상황에서 어떻게 고칠 수 있는지 알아보자.
시간이 지남에 따라 메모리 사용량이 점점 증가하는걸 원치 않을 것이다.
아래와 같은 상황은 원치 않는다.
아래와 같은 상황을 원한다.
가장 쉽게 메모리 사용량을 확인하는 방법은 Xcode 안에 있는 ‘Debug Navigator’에서 보는것이다. ‘Memory’ 패널을 선택하고 실시간 메모리 사용량을 볼 수 있다. 여기서는 메모리 사용량이 계속 증가하거나 한번도 낮아지지 않는 문제와 같은 문제들을 바로 확인하는데 도움을 준다.
메모리 사용량을 더 자세히 보기위해 ‘Profile in Instruments’를 누르면 이 세션에서 transfer할건지 새로 하나 만들면서 (Instruments를)restart할건지 물어볼 것이다. restart를 누른다. 필자는 transfer을 눌러서 뭔가 정보를 손실하거나 그런 좋지않은 경험이 많다. 그러면 Allocations and Leaks 템플릿을 가지는 Instruments가 열리고 이것은 모든 메모리 할당과 모든 발생하는 잠재적인 누수를 눈으로 볼 수 있다.
이번에도 누수 부분을 깊게 들어가진 않겠지만, 메모리가 할당은 됬으나 해제가 되지않는 것들을 보기 위해 특정 시간 간격 안에서 스냅샷을 찍어볼 것이다. 메모리 누수는 Objective-C나 C 라이브러리 같은 것을 사용하면 종종 나타난다. 웬만하면 ‘Analyze’ 빌드 옵션을 사용하여 찾을 수 있으나, 간혹 이 Analyze 툴이 아무것도 찾지 못할때도 있다. 반면 Swift에서 작업하고 있으면 Objective-C를 사용할때보다 누수를 적게 겪을 것이다.
메모리 프로파일러를 쓰면서 내가 찾은 유용한 기능은 두가지이다. 첫째로 각 순서가 이후에 동시에 이벤트의 순서를 수행한다는 것. 둘째로 메모리 마킹을 생성하는 것이다. 이걸 사용하면 당신은 각 스냅샷 사이에 메모리의 변화를 분석할 수 있다. 오른편 ‘Display Settings’ 섹션 위에 있는 ’Mark Generation’ 버튼을 누르면 바로 메모리 스냅샷을 찍을 수 있다. 진행되는 동안 마크를 만드는걸 까먹었어도 언제든지 마크를 추가한 뒤에 단지 클릭하고 표시를 상단에 놓고 움직이면 원하는 지점으로 마크를 이동시킬 수 있다. 그러고나서 ‘Allocation Type’을 ‘All Heap Allocations’으로 바꾸면, 우리가 손대기 힘든 시스템의 정보들은 숨기고 실 증가량에 따라서 정렬해준다. 이제 당신은 범위동안의 메모리 사용량을 보기 쉽게 확인할 수 있을것이나, 이것은 실제 당신이 생성한 오브젝트 자체를 확인하기는 좀 어려울 것이다.
이제 메모리 할당에 따른 리스트는 가지고 있으니, 이것이 제대로 나타나있는지 확인하야한다. 사실 초기에 표시되는 데이터들은 좀 쓸모가 없다. 만약 Swift를 사용하고 있다면, 모든 Swift 객체들의 이름 앞에 앱 이름이 붙을것이고 거기서 앱 이름으로 필터링하면 당신의 객체를 찾을 수 있다. 반면 Objective-C를 사용하고 있다면 객체를 찾아내는데 좀 특이한 방법을 사용한다. 당신이 찾고 싶은 것의 이름을 알고 있을것이다. 당신의 파일이나 객체 이름에 접두로 어떤 특정 이름을 붙인다면 찾을 수 있다. 예를들어 당신의 모든 view controller들이 *ViewController와 같은 이름을 가진다면 ‘ViewController’로 검색해도 찾을 수 있을것이다.
여기 필자가 만든 예제에는 4개의 스냅샷이 있고 ViewController라는 이름으로 객체들을 필터링 했다. 그리고 각 스냅샷 사이에 ServiceViewContoller에서 누수가 일어났다는걸 볼 수 있다.
여기 이 글의 목적을 위해 ViewController에다 뭔가 문제가 있는 retain cycle을 집어넣으나, 원래는 코드를 뒤져보면서 어떤것이 객체의 누수를 만드는지 일일히 살펴봐야한다. retain cycle은 두 객체가 서로 강한 참조를 하고있어서 서로 할당 해제되는걸 막을때 일어난다. 필자는 retain cycle에대해 더 깊게 말하진 않을것이나, 이런 경우에 delegate나 blocks/closures를 먼저 확인하는 것을 추천한다. 당신의 delegates가 약(weak)하고 blocks/closures에서 약한 참조를 사용한다는걸 확인하면 좋을것이다.
당신의 객체에서 다른 객체를 참조하고있어서 그 다른 객체가 메모리 해제되지 못하는지 일일히 확인하기 좀 힘들다면, Instruments를 사용하여 더 많은 객체들의 자료를 얻을 수 있을 것이다. Instruments에서 객체 옆에 작은 화살표를 누르면, 그 객체의 모든 할당된 객체들을 보여줄 것이며 누가 생성하였는지까지도 나온다. 그다음 또 작은 화살표를 누르면 retain과 release count 정보까지 얻을 수 있다. 만일 count가 0이 되지 않는다면 메모리 해제가 되지 않았다는 뜻이다. 여기서 팁을 주자면 ‘Responsible Library’는 당신 코드이니 유심히 보고 ‘libsystem_blocks’도 유심히 봐라. 반대로 ‘UIKit’은 스킵해도 된다. 필터링하기위해 이 아이템들을 검색 박스에 쳐볼 수 있다. 그러고나면 ‘ Extended Detail’이 보이고 이것들이 어떻게 돌아가고있는지 스택 trace가 보이게 될것이다.
결론
이 글은 Instruments의 아주 일부만 보여준 것이다. 그래도 Instruments를 시작하는 사람들에게 도움이 되길 바라며 처음부터 바로 잘하지 못해도 괜찮다. 툴을 사용하는데 점점 더 익숙해지다보면, 당신의 코드가 더 나은 코드가 될것이다. 당신은 문제를 만든 이슈를 찾는것에 주도적이게 될 것이며, 앱 프로파일링이 당신의 익숙한 개발 플랜 중 하나의 플랜으로 자리잡게 될 것이다.
Instruments에대해 더 배우고 싶다면 필자는 2014년 WWDC 비디오(Improving your app with Instruments)를 보는것을 적극 추천한다. 여기서는 추가적인 팁엔 트릭과 함께 살펴볼 수 있다. Instruments에대해 더 읽고싶으면 애플이 만든 유저 가이드를 확인해보길 바란다.