Question 2
Using Instruments effectively
An app feels slow, but the cause isn't obvious. How do you use Instruments to narrow it down, and which templates do you usually start with?
Follow-ups
- Time Profiler vs Allocations vs Leaks vs Memory Graph?
- What patterns do you look for first?
Answer outline
Match the symptom you can see to the right template first, then drill into the patterns inside it:
- 1.CPU or UI hitches: start with Time Profiler. Filter to the main thread, find the hottest symbols and call trees, and look for synchronous I/O, JSON decoding, and regex in hot paths.
- 2.Memory climbing: run Allocations, repeat an action, and watch net growth. Unbounded growth usually means a cache without eviction or a retained closure, and pressing Mark Generation between passes shows what survived each one.
- 3.Suspected leaks: run Leaks. It flags allocations that nothing can reach anymore, which is the classic leak signature.
- 4.Retain cycles (or orphaned view controllers): open Memory Graph in Xcode and follow the reference chains back to whatever keeps the object alive. Strong delegate references and closure captures are the usual links.
Once you're inside a template, work top down. Sort by weight, then turn on Invert Call Tree and Hide System Libraries so the hot spots in your own code surface first. Confirm the finding on a second trace before you change anything.
Principles
- Profile on a physical device in a Release build with realistic data, because Debug builds and empty databases lie.
- Take a baseline trace before any fix, so you can prove a change helped instead of assuming it did.
- Don't chase tiny wins like
objc_msgSenduntil the big bottlenecks are gone. - Name your own phases with
os_signpostintervals, and vague slowness turns into regions you can compare.
An os_signpost interval in the Points of Interest category gets its own track, so your phases line up against the timeline:
import os.signpost
let log = OSLog(subsystem: "com.app", category: .pointsOfInterest)
func loadPage() async throws {
os_signpost(.begin, log: log, name: "LoadPage")
defer { os_signpost(.end, log: log, name: "LoadPage") }
// Fetch and decode the page here
}
Follow-up angles
- When battery drain or heat is the complaint, use the Energy Log template on a device. It lines up CPU, location, and network activity so you can see which one is burning power.
- System Trace shows thread scheduling and priority inversion, which is where to look when background work is blocking the UI.



