Understanding LLDB print commands for iOS debugging with Xcode
Learn the difference between po, p, and v to inspect variables in Xcode's console.
19 May 2025 · 4 min read
LLDB is a powerful tool for exploring and debugging iOS applications at runtime.
When we set a breakpoint in our code, Xcode pauses the app and gives us a few ways to inspect the current state. We can either use the variable viewer in Xcode's sidebar or enter LLDB commands directly in the console.
To inspect variables through LLDB, we typically use three print commands:
Let's walk through how each one works and when to use them.

LLDB command v
The v command is an alias for frame variable. It reads what's currently in memory and prints an LLDB-formatted summary of the value. Xcode uses this command behind the scenes for its variable viewer.
(lldb) v bird(ExampleApp.Bird) bird = {name = "kea"conservationStatus = en}
This command is fast and safe because it doesn't compile or evaluate any expressions. It only works with stored properties - computed properties or function calls will result in an error:
(lldb) v bird.conservationStatus.titleerror: "title" is not a member of "(ExampleApp.ConservationStatus) bird.conservationStatus"
LLDB command p
The p command is an alias for expression. Unlike v, it compiles and executes code on the spot, which means we can evaluate expressions, access computed properties, and call functions.
(lldb) p bird(ExampleApp.Bird) $R2 = {name = "kea"conservationStatus = en}
For a computed variable:
(lldb) p bird.conservationStatus.title(String) $R3 = "endangered"
LLDB command po
The po command is also an evaluated expression, but it prints a human-readable description of the object instead of the LLDB-formatted structure. Internally, it calls expression --object-description.
If the object conforms to CustomDebugStringConvertible, po uses the custom debug description we provide.
extension Bird: CustomDebugStringConvertible {var debugDescription: String {return "The bird \(name) is \(conservationStatus.title)"}}
Which results in the following:
(lldb) po bird▿ The bird kea is endangered- name : "kea"- conservationStatus : ExampleApp.ConservationStatus.en
If no custom description is provided, po falls back to the system’s default.
Conclusion
In most cases, using p or po gives us the most flexibility. They evaluate expressions and work with computed properties and methods. However, they do add a bit of runtime overhead since they compile and execute code.
If we're dealing with timing-sensitive code, such as multithreading bugs or race conditions, it may be better to use the v command. It doesn't disturb the execution environment as much because it doesn't evaluate code, just reads memory.
For these situations, we can even set breakpoints that don't pause the app. In Xcode's breakpoint editor, we can enable the option to Automatically continue after evaluating actions and log the output with v, so we can debug without affecting the timing.



Newsletter
Like to support my work?
Say hi
Related tags
Articles with related topics
Latest articles and tips



