Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 스위프트
- combine
- Observable
- map
- tableView
- uitableview
- swift documentation
- MVVM
- rxswift
- uiscrollview
- clean architecture
- collectionview
- 애니메이션
- ribs
- HIG
- ios
- UICollectionView
- Refactoring
- SWIFT
- Human interface guide
- 리팩토링
- Protocol
- RxCocoa
- 리펙터링
- 리펙토링
- Xcode
- swiftUI
- 클린 코드
- Clean Code
- UITextView
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 2. UI 성능 분석 - Instrument의 Animation Hitches 사용하여 UI 성능 개선하기 본문
iOS 응용 (swift)
[iOS - swift] 2. UI 성능 분석 - Instrument의 Animation Hitches 사용하여 UI 성능 개선하기
jake-kim 2023. 1. 28. 23:151. UI 성능 분석 - Render Loop 이해하기 (Commit, Layout, Display, Prepare, Commit)
2. UI 성능 분석 - Instrument의 Animation Hitches 사용하여 UI 성능 개선하기 <
3. UI 성능 분석 - Commit Hitch를 줄이는 UI 성능 최적화 프로그래밍
1번 글에서 알아본 2가지의 hitch
1) Commit hitch
- commit 단계: UI를 변경하고 이 변경을 랜더 서버에게 알리는 역할
- 주어진 시간내에 commit하지 못하는 경우
- commit하지 못한 경우 이전 프레임을 그대로 보여줌
- “hitch time”: 위와같이 지연되는 시간을 의미하며 ms로 측정
2) Render hitch
- Render 단계: commit단계에서 변경된 UI를 바탕으로 GPU를 사용하여 렌더링하는 역할
- 주어진 시간내에 랜더 서버에서 랜더링하지 못하는 경우
-> 이번 글에서는 Commit hitch를 잡는 내용
Commit 개념 (복습)
- commit 한다는 의미?
- 사용자가 버튼을 탭 > 뷰 UI 내부적으로 계산하여 업데이트 > 업데이트된 layer tree를 GPU에게 제출하는 행위
- Commit Hitch란?
- Commit 단계가 지연되어 이전 프레임을 보여주어 끊기는 듯한 현상 발생
Commit Transaction의 4가지 단계
- Layout
- 레이아웃 변경 단계
- 변경이 필요한 subview들의 레이아웃이 layoutSubviews()가 호출되며 변경되는 단계
- 언제? frame, bounds, transfrom, 뷰 추가 삭제, setNeedsLayout() 호출되는 경우
- Display
- content를 업데이트 되는 경우 호출
- 언제? 오버라이딩한 draw()를 호출할 때, setNeedsDisplay() 호출할 때
- Prepare
- 이미지에 대한 작업을 처리 (디코딩 되지 않은 이미지를 디코드)
- GPU에서 지원하지 않는 포멧의 이미인 경우 이 단계에서 처리
- Commit
- layer tree를 pacakge하여 render 서버에 전송하는 역할
Instruments로 commit hitch 찾고 제거하기
* 테스트 할 코드
- cmd + I 로 instrument 실행 (프로파일)
- Animations Hitches 선택
- record 버튼 클릭
- Record 버튼을 누르고 UI 관련 작업을 한 후 Stop 버튼 클릭 > 자동으로 분석 > 아래 화면 확인
- 아래 로깅에서 Severity, Hitch Type확인
- Severity - Moderate (보통의), High 등
- Hitch Type - hitch의 유형
(Severity high인 것들을 대략적으로 확인하고 Hitch Duration이 큰 값을 확인 - 7번 Hitch ID가 duration이 33.33ms보다 높으므로 frame이 지연되어 버벅거리게 되는 버그 발생)
- Main 프로세스를 찾고, 어떤 코드에서 발생했는지 유추
- filter 검색에 자신의 앱 이름을 검색 (예제에서 사용한 앱 이름은 ExUIDebugger)
- 검색 후 아래처럼 Main Thread 선택
- Profile 선택 > 오른쪽 Havial Stack Trace에서 UI관련코드 선택
* Havial Stack Trace는 duration이 많이 걸리는 부분을 추천해주어서 디버깅 할 때 매우 편리
- 실제로 30ms가 발생하므로 이 부분 체크
- 밑에 layoutSubviews인 곳도 12ms나 차지하고 있으므로 이 부분도 체크
- 위에서 얻은 단서를 토대로 MyCell이라는 클래스의 prepare(titleText:)메소드와 그 안의 layoutSubviews 관련 코드 확인
- MyCell코드를 보니 prepare에서 계속 titleLabel 인스턴스를 지우고 생성하므로 재사용성에 안좋은 코드이며, layoutIfneeded()를 매번 호출하므로 UI성능에 안좋은 효과
- draw(_:) 메소드를 오버라이딩 했는데, 아무런 구현이 없고 오버라이딩 하기만 하더라도 UI 성능에 안좋으므로 필요하지 않다면 삭제 필요 (구체적인 내용은 다음 챕터에서 계속)
import UIKit
final class MyCell: UITableViewCell {
private var titleLabel: UILabel?
override func prepareForReuse() {
super.prepareForReuse()
prepare(titleText: nil)
}
func prepare(titleText: String?) {
guard let titleText else {
titleLabel?.removeFromSuperview()
return
}
let titleLabel = UILabel()
titleLabel.text = titleText
titleLabel.textColor = .black
titleLabel.font = .systemFont(ofSize: 24)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(titleLabel)
NSLayoutConstraint.activate([
titleLabel.leftAnchor.constraint(equalTo: contentView.leftAnchor),
titleLabel.rightAnchor.constraint(equalTo: contentView.rightAnchor),
titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor),
])
self.titleLabel = titleLabel
layoutIfNeeded()
}
override func draw(_ rect: CGRect) {
super.draw(rect)
}
}
- 아래처럼 개선하면 좋은 코드
- titleLabel 매번 인스턴스 생성 안하도록 수정 (재사용)
- layoutIfNeed() 삭제
- draw(_:) 오버라이딩 삭제
import UIKit
final class MyCell: UITableViewCell {
private var titleLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
titleLabel.textColor = .black
titleLabel.font = .systemFont(ofSize: 24)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(titleLabel)
NSLayoutConstraint.activate([
titleLabel.leftAnchor.constraint(equalTo: contentView.leftAnchor),
titleLabel.rightAnchor.constraint(equalTo: contentView.rightAnchor),
titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor),
])
}
required init?(coder: NSCoder) {
fatalError()
}
override func prepareForReuse() {
super.prepareForReuse()
prepare(titleText: nil)
}
func prepare(titleText: String?) {
titleLabel.text = titleText
}
}
* (Hitches commit이 있는) 전체 코드: https://github.com/JK0369/ExUIDebugger
* 참고
'iOS 응용 (swift)' 카테고리의 다른 글
Comments