Time Profiler
What is Instruments?
Instruments is Apple performance analysis tool that profiles your app in real time. It is included with Xcode and provides a suite of specialized tools for measuring CPU, memory, energy, and network usage.
Opening Instruments
In Xcode, go to Product, Profile (Cmd+I). This builds your app in Release mode and opens Instruments with your app running. Select the Time Profiler template to begin CPU analysis.
Understanding Time Profiler
Time Profiler periodically samples your app call stack to determine which functions consume the most CPU time. The key views are:
- Track: Shows CPU usage over time as a timeline graph
- Call Tree: Hierarchical view of function call costs
- Detail: Shows source code with attributed timing data
- Extended Detail: Full call stack for selected sample
Reading Time Profiler Data
Focus on these columns in the Call Tree:
- Self Time: Time spent in the function itself (not its callees)
- Total Time: Time spent including all sub-calls
- Sample Count: How many times the function appeared in samples
Common Optimizations
// Before: Unnecessary work in tight loop
func processItems(_ items: [Item]) {
for item in items {
let formatted = NumberFormatter.localizedString(from: NSNumber(value: item.price), number: .currency)
item.displayPrice = formatted
}
}
// After: Cache formatter, avoid repeated allocation
private static let priceFormatter: NumberFormatter = {
let f = NumberFormatter()
f.numberStyle = .currency
return f
}()
func processItems(_ items: [Item]) {
for item in items {
item.displayPrice = Self.priceFormatter.string(from: NSNumber(value: item.price)) ?? ""
}
}
Profiling Best Practices
- Always profile in Release mode, not Debug
- Use a physical device for realistic performance
- Run multiple iterations to get stable measurements
- Focus on the hottest functions (highest self time)
- Look for unexpected allocations in tight loops
- Use the Highlight button to isolate specific time ranges
Memory Graph & Leaks
Memory Instruments
Instruments provides several memory-related tools:
- Leaks: Detects memory leaks from retain cycles
- Allocations: Tracks all memory allocations
- Memory Graph: Visualizes object reference graphs
- Zombies: Detects use-after-dealloc errors
Detecting Memory Leaks
The Leaks instrument monitors your app memory and flags objects that are allocated but never deallocated. Common causes:
// Retain cycle in closure
class NetworkManager {
var onComplete: (() -> Void)?
func fetchData() {
URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
// self is captured strongly here if not using [weak self]
self?.processData(data)
self?.onComplete?() // Double retention
}.resume()
}
}
// Fix with capture list
class NetworkManager {
var onComplete: (() -> Void)?
func fetchData() {
URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
guard let self = self else { return }
self.processData(data)
self.onComplete?()
}.resume()
}
}
Memory Graph Debugger
Xcode built-in Memory Graph Debugger (Cmd+Shift+M) shows a visual graph of all live objects and their relationships. Red indicators show likely retain cycles.
Allocations Instrument
Track every allocation to find memory hogs:
- Sort by Total Bytes to find the largest allocations
- Check Persistent count for objects that should be deallocated
- Use Mark Generation to compare memory between time points
// Bad: Creating objects unnecessarily
func loadData() {
let items = (0..<10000).map { _ in
HeavyObject() // Each one allocates significant memory
}
// Use lazy loading instead
}
// Good: Lazy loading with pagination
func loadPage(page: Int, size: Int) async throws -> [Item] {
return try await api.fetchItems(offset: page * size, limit: size)
}
Memory Warnings
Monitor memory warnings in your app. When the system sends didReceiveMemoryWarning, you should release non-essential cached objects. Profile with the VM Tracker instrument to see your app virtual memory usage.
Energy Diagnostics & Network
Energy Diagnostics
Battery life is critical for mobile apps. The Energy Impact instrument shows how your app affects battery:
- CPU usage: High CPU drains battery quickly
- Network activity: Frequent small requests waste energy
- Location services: GPS is extremely power-hungry
- Display: Screen brightness and animation frame rate
Energy Impact Levels
Instruments classifies energy impact as:
- Minimal: Less than 5% battery impact
- Low: 5-15% impact
- Medium: 15-30% impact
- High: Above 30% impact (avoid this)
Network Profiling
The Network instrument shows all network activity:
- Connection types: WiFi vs Cellular
- Request/response sizes: Identify oversized payloads
- Timing: Find slow endpoints
- DNS lookups: Repeated DNS resolution wastes energy
Optimizing Network Efficiency
// Bad: Multiple small requests
func loadDashboard() async {
let user = try await api.fetchUser() // Request 1
let posts = try await api.fetchPosts() // Request 2
let notifications = try await api.fetchNotifications() // Request 3
}
// Good: Batched or combined request
func loadDashboard() async {
let dashboard = try await api.fetchDashboard() // Single request
// Or use async let for parallel requests
async let user = api.fetchUser()
async let posts = api.fetchPosts()
async let notifications = api.fetchNotifications()
let (u, p, n) = await (try user, try posts, try notifications)
}
Location Accuracy
Choose the right location accuracy for your use case:
// Battery-efficient: approximate location
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
// When you need precision
locationManager.desiredAccuracy = kCLLocationAccuracyBest
// Disable location when not needed
locationManager.stopUpdatingLocation()
Background Energy
Use BGTaskScheduler for efficient background work:
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.app.refresh",
using: nil
) { task in
self.handleRefresh(task: task as! BGAppRefreshTask)
}
let request = BGAppRefreshTaskRequest(identifier: "com.app.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 3600)
try await BGTaskScheduler.shared.submit(request)
The system batches background tasks to minimize wake-ups and preserve battery.
Quiz
1. What does the Time Profiler instrument measure?
2. Which instrument detects memory leaks from retain cycles?
3. Why should you profile in Release mode?
4. What is the most energy-efficient location accuracy?
Flashcards
Question
What does Self Time mean in Time Profiler?
Click to reveal answer
Answer
The time spent executing the function itself, excluding time spent in any functions it calls. Focus optimization on functions with high Self Time.
Question
What causes memory leaks in iOS?
Click to reveal answer
Answer
Retain cycles where objects hold strong references to each other, preventing deallocation. Common in closures, delegates, and notification observers.
Question
How do you reduce network energy impact?
Click to reveal answer
Answer
Batch requests, use appropriate cache policies, avoid polling, and choose the right quality of service for background tasks.
Revision Notes
Key Takeaways
- 1. Profile in Release mode for accurate measurements
- 2. Focus optimization on functions with highest Self Time
- 3. Use Leaks instrument to find retain cycles
- 4. Batch network requests to save energy
- 5. Choose appropriate location accuracy for your use case
Interview Tips
- • Explain how you would identify and fix a performance bottleneck
- • Describe common causes of memory leaks in iOS apps
- • Discuss strategies for reducing battery consumption
- • Walk through using Instruments to profile a specific feature
Cheat Sheet
Instruments Quick Reference
- Time Profiler: CPU sampling, focus on Self Time
- Leaks: Detects retain cycles
- Allocations: Tracks all memory allocations
- Energy Impact: Battery usage analysis
- Network: Request/response monitoring
- Always profile in Release mode on real device