Instruments: Xcode's Profiling Suite

Xcode: The IDE Itself

Chapter 5 ยท Instruments: Xcode's Profiling Suite

Chapter 4's debugger answers "what is this code doing right now." Instruments answers different questions: where is the time going, and where is the memory going. This chapter introduces the tool and its three most-used instruments, Time Profiler, Allocations and Leaks. iOS Development — Architecture & Data 7 already used them at the workflow level, so this chapter stays at the tool level. It also corrects one point from that chapter, explained below.

What Instruments Is

Instruments is Apple's application performance analyzer, integrated into Xcode 3.0 and later. It is built on top of the DTrace tracing framework from OpenSolaris, which was ported to Mac OS X v10.5. Its timeline can show CPU activity across processes and threads, memory allocation and leak detection, file I/O, network traffic, graphics performance, energy diagnostics and UI automation. It has customizable "instruments" for monitoring event groups, an Instrument Builder for making your own, and the ability to record and replay user events so the same test can be repeated. The reference lists its current version as 26.0, from September 2025.

Sampling Versus Tracing
Sampling looks at the CPU every few milliseconds to see what code is running. Tracing records every time a specific event, such as a memory allocation, occurs. That difference explains why a sampling profiler shows where time is spent overall but can miss something very brief, and why a tracing instrument can be more detailed but heavier.

Start From a Release Build

Profiling begins in the Product menu, with Profile. This builds your app in Release mode, which the tutorial says uses the same optimizations and configurations as your production build. That connects to Chapter 2: build configurations decide how your code is compiled, and Debug and Release differ. Timings taken from a Debug build can mislead you about what users will see. I could not confirm the details of how Xcode chooses the configuration for Profile from Apple's documentation, so check the scheme's Profile action in your own project.

Three Instruments

InstrumentQuestion it answersHow it works
Time ProfilerWhich code is using the CPU?Samples the call stacks of running threads, typically at 1 ms intervals, to build a statistical profile of where time goes
AllocationsWhat is my app holding in memory, and is it growing?Tracks heap allocations: the creation, growth and persistence of objects
LeaksIs memory unreachable but never freed?Takes periodic heap snapshots and scans for unreferenced blocks in writable memory, registers and stacks

Reading Time Profiler

The call tree has columns for weight, self and symbol name. Weight is the percentage of samples in which that call tree appeared; self is the time spent in that method itself, excluding what it calls. Tutorials describe the heaviest stack trace on the right as usually all you need to solve a performance issue. That is a rule of thumb from one tutorial, not a guarantee.

Reading Allocations

Two figures matter. Persistent Bytes is the total your app currently holds in memory, allocated and not yet freed. Transient is the total number of allocations that have been freed. A number that keeps climbing as you repeat an action, such as opening and closing a screen, is the usual sign of something not being released.

Reading Leaks

Leaks identifies allocations without retainers and presents them with address, size and allocation stack traces, so you can trace back to the code that created them. It also has a Cycles & Roots view, which shows retain cycles as a graph.

A Correction to Architecture & Data 7
iOS Development — Architecture & Data 7 stated that the Leaks instrument technically cannot detect a strong reference cycle, and that Allocations is the correct tool for one. The sources I found for this chapter say something more nuanced. Leaks does detect retain cycles in some cases and shows them under Cycles & Roots. Its detection works by root analysis: from a set of live references it computes what is reachable, and anything not reachable is a leak. So a cycle that nothing live points to can be reported. What it can miss is a cycle that is still referenced from something alive; one tutorial describes a timer holding a view controller that Leaks did not flag, probably because it was not a true leak. The safe statement is: Leaks may miss cycles that live objects still reference, so a growing Persistent Bytes in Allocations remains a useful second check. I have not tested this myself, and I have not yet changed the earlier chapter.

A Profiling Routine

  1. Choose Product → Profile so you are measuring a Release build, and pick the instrument that matches your question.
  2. Reproduce the slow or growing behavior in the app while recording.
  3. In Time Profiler, follow the heaviest path in the call tree to the code that dominates.
  4. In Allocations, repeat the action several times and watch whether Persistent Bytes keeps rising.
  5. Fix one thing, profile again, and compare, because a change that seems obvious can change nothing measurable.

Hands-On Exercises

Exercise 1

Explain the difference between sampling and tracing, and say which one Time Profiler uses and which kind of question each is better suited to.

๐Ÿ“„ View solution
Exercise 2

A screen feels slow when opened, and after opening and closing it ten times the app's memory is noticeably higher. Say which instruments you would use for each symptom and what you would look at in each.

๐Ÿ“„ View solution
Exercise 3

Rewrite the sentence "Leaks cannot detect retain cycles" so it is accurate according to this chapter's sources, and say what you would still want to test yourself.

๐Ÿ“„ View solution

Chapter 5 Quick Reference

  • Instruments: Apple's performance analyzer; built on DTrace; integrated into Xcode 3.0 and later
  • Start: Product → Profile, which builds a Release build
  • Time Profiler: samples call stacks, typically every 1 ms; read weight and self columns
  • Allocations: Persistent Bytes (still held) and Transient (freed)
  • Leaks: unreferenced blocks; Cycles & Roots view; may miss cycles that live objects still reference
  • Unverified: how Xcode picks the configuration for Profile, the leak-versus-cycle nuance from my own testing, and the earlier chapter's wording