- 왜 성능을 고려해야되는가?: 성능은 기능이다!(Performance is feature)
- 어떻게 성능을 고려할까
- 기술을 고르고
- 측정하고
- 목표를 설정한다.
- 이를 기반으로 코드를 수정한다.
- 기술을 고른다.
- 예시
- 적은 양의 데이터는 UserDefaults
- 많은 양의 데이터는 Core Data
- 애플 API를 쓰면 플랫폼에 최적화 되어 있고, 소프트웨어 업데이트 시에 성능 향상을 기대할 수 있다.
- 측정한다.
- 애니메이션 -> Core Animation Instruments
- 반응성(유저 입력에 어떻게 반응하는가?)
- 코드 측정 -> 제출하는 빌드에는 섞여들어가지 않도록 하라
- 시간 측정(CFAbsoluteTimeGetCurrent)
- system trace
- 메모리
- Xcode debugger
- Allocations, Leaks
- 목표 설정
- 60 fps(관련 영상: Advanced Graphics and Animations for iOS Apps - WWDC 14)
- 100ms 안에 유저 입력에 반응 한다
- 퍼포먼스 워크플로우
- 유념할 사항
- 추측 금지(가설을 이야기하는 것은 아니다)
- 성급한 최적화를 피하라
- 한번에 하나만 고쳐라
- 일반적인 디버깅같이 하라.
- Reproduce -> Profile -> Measure -> Change 의 반복
- Profiling vs Measuring
- Profiling: 전반적인 앱 활동을 이해하는 것
- Xcode debugger
- Instruments: Time Profiler
- Measuring: 특정 액션에 대해서 실험하는 것
- CFAbsoluteTimeGetCurrent
- Instruments: System Trace
- 다시 반응성
- 모든 유저 입력을 받는 곳은 메인스레드다. -> 메인스레드의 성능 개선은 필수다.
- 메인 스레드에서 하지 말아야 할 것
- CPU를 집중적으로 쓰는 작업
- 외부 리소스에 의존하는 작업(네트워킹, 디스크 로딩)
- 블로킹 콜을 주의하라(Synchronous)
- 시스템 콜을 유발하는 코드 흐름
- 메모리에 존재하지 않는 리소스에 접근하려고 하면 발생한다. -> 한번에 하나만 발생해야 하기 때문에
- 블로킹 콜은 그에 상응하는 비동기 API를 제공하는 경우가 많다.
- 하지만 순서에 의존적이면 적용할수 없기 때문에 구조 자체를 바꿔야 한다.
- 상응하는 비동기 호출이 없다면?
- GCD를 쓰자
- GCD는 글로벌 스레드 풀을 관리한다.
- GCD는 태스크를 클로저(블록)으로 다룬다.
- 어떤 스레드에서 실행될 지는 알 수 없다.
- 스레드 안정성
- 메인 스레드에만 접근하도록 강제하는 경우
- 어떤 스레드에서도 접근할 수 있지만, 보호 장치가 없는 경우 -> 씨리얼 큐를 사용하면 보호할 수 있다.
- 메모리
- 필요성
- 멀티태스킹은 메모리 튜닝을 요구한다.
- watchOS 같은 경우는 특히 더 메모리가 모자란다.
- 옛날 하드웨어를 지원하는 경우도 흔하다.
- 익스텐션 같은 경우에도 메모리가 귀한다.
- iOS 메모리 시스템
- 이것저것 다 하기에는 턱도 없다.
- 정지된 앱은 보존되지 않는다.
- 저장 단계 없이 메모리에서 추방될 수 있다.
- 메모리와 시간
- 메모리를 할당 받는 것은 시간이 필요하다.
- 갑자기 큰 메모리를 요구하면 반응성에 영향을 미친다. -> 다른 것들을 추방해야 되니까
- 메모리 소모량이 작으면, 백그라운드에서도 추방될 가능성이 줄어든다.
- 메모리 사용을 합리적으로 하기 위해서는?
- 어떤 리소스를 사용하는가?
- 문자열
- 이미지
- 코어 데이터 Managed 객체
- 리소스 사용에 대한 멘탈 모델을 만들어라
- Xcode 디버거로 메모리 사용을 확인하라.
- NSCache를 쓰면 리소스 관리를 간단히 할 수 있다 -> 라이프 사이클 이벤트에 자동으로 반응해준다.
- 커스텀할거면, 특정 노티피케이션에 반응하도록 하라
- DidEnterBackground
- DidReceiveMemoryWarning
- iOS에서는 되는게, WatchOS에서는 안될 수도 있다.
- 짧고, 간단한 인터렉션을 기대한다.
- 최근 데이터, 알람 등을 확인하기 위한 목적이 크다
- 런치 타임은 크리티컬하다.
- 이를 위해서, 생각을 바꿔야 한다.
- 적절한 크기와 포맷으로 필요한 것만 서버에서 보내야 한다.
- 안 쓰는 데이터(JSON 키, XML blob)는 최대한 배제해야 한다.
- 이미지 사이즈도 적절하게 줄여서 보내줘야 한다.
- 앱 컨텍스트를 최신으로 유지해야 한다.
- 양방향 업데이트가 되는 공유 상태
- WCSession.defaultSession().updateApplicationContext(…)
- 백그라운드에서 앱을 리프레시 하면 이득이 크다.
- 네트워크 요청은 최소한으로
- 이미 있는 서버 로직을 쓰는 경우는, 아이폰에 간단한 서비스를 만들어서 거기서 서버 결과를 파싱하면 된다.
- WCSession.defaultSession().sendMessage(…)