Section 6/181 menit
6. Profiling dengan Instruments
6. Profiling dengan Instruments
Instruments adalah profiling toolkit Apple yang terintegrasi dengan Xcode. Aksesnya via Xcode → Product → Profile (⌘ + I).
Time Profiler: Cari Bottleneck CPU
swift
// Sebelum profiling, pastikan:
// 1. Build dengan Release configuration (bukan Debug)
// 2. Simulasi real user behavior, bukan edge case
// 3. Run on device fisik (bukan simulator) untuk hasil akurat
// Workflow Time Profiler:
// 1. ⌘ + I → pilih "Time Profiler"
// 2. Record selama 10–30 detik sambil reproduce masalah
// 3. Stop recording
// 4. Lihat "Heaviest Stack Trace"
// 5. Filter: ✓ "Hide system libraries" untuk fokus ke kode kita
// 6. Double-click frame untuk buka kode di Xcode
// Contoh kode yang bakal ketahuan lambat:
// ❌ JSON parsing di main thread
func loadData() {
let data = try! Data(contentsOf: fileURL) // blocking I/O
let items = try! JSONDecoder().decode([Item].self, from: data) // heavy parsing
self.items = items // UI update
}
// ✓ Setelah profiling, fix dengan background processing
func loadData() {
Task.detached(priority: .userInitiated) {
let data = try Data(contentsOf: fileURL)
let items = try JSONDecoder().decode([Item].self, from: data)
await MainActor.run { self.items = items }
}
}
Memory Graph Debugger: Cari Retain Cycle
swift
# Di Xcode Debug Navigator (⌘ + 7):
# Saat app running → klik ikon memory graph (segitiga bertumpuk)
# Xcode generate snapshot memori saat itu
# Tanda retain cycle: objek masih ada padahal seharusnya sudah di-deinit
swift
// ❌ Retain cycle klasik: closure capture strong self
class ProfileViewModel {
var onUpdate: (() -> Void)?
func loadProfile() {
APIClient.shared.fetchProfile { response in
self.handleResponse(response) // ← strong capture!
self.onUpdate?() // ← strong capture!
}
}
}
// ✓ Fix dengan [weak self]
class ProfileViewModel {
var onUpdate: (() -> Void)?
func loadProfile() {
APIClient.shared.fetchProfile { [weak self] response in
guard let self else { return }
handleResponse(response)
onUpdate?()
}
}
}
// ✓ Dengan Swift Concurrency: tidak perlu [weak self] pada Task
@MainActor
class ProfileViewModel {
var profile: Profile?
func loadProfile() {
Task {
// self di-capture sebagai weak oleh Swift Concurrency runtime
// tapi tetap safe karena Task diikat ke lifecycle actor
let result = try await APIClient.shared.fetchProfile()
self.profile = result
}
}
}
Allocations: Cari Memory Leak & Excessive Allocation
swift
Instruments → Allocations template:
1. Record sambil navigate bolak-balik ke sebuah screen
2. Gunakan "Generation" feature:
Mark A → navigate ke screen → Mark B → navigate keluar → Mark C
3. Lihat objek yang masih ada di Mark C tapi seharusnya sudah hilang
4. Click objek → "Backtrace" untuk lihat di mana dialokasikan
Metrics yang perlu diperhatikan:
- All Allocations: total memory yang pernah dialokasi
- Live Bytes: memory yang sedang aktif digunakan
- # Living: jumlah instance yang masih hidup
Leaks: Auto-detect Memory Leak
swift
Instruments → Leaks template:
Lebih mudah dari Allocations — Instruments otomatis flag memory leak
dengan warna merah saat ditemukan.
Pastikan:
- Record minimal 30 detik
- Buka/tutup screen yang diduga bocor minimal 3x
- Lihat "Leaked Objects" di timeline
Network: Analisis Request & Response
swift
Instruments → Network template:
Menampilkan:
- Semua HTTP/HTTPS request dengan latency
- Upload & download size
- Connection pooling & timing (DNS, TCP connect, TLS, TTFB, transfer)
Berguna untuk:
- Cari request yang unexpectedly slow
- Detect payload yang terlalu besar
- Verify caching behavior