Logo for tanaschita.com

Understanding LLDB print commands for iOS debugging with Xcode

Learn the difference between po, p, and v to inspect variables in Xcode's console.

11 Jul 2022 · 4 min read

LLDB is a powerful tool for exploring and debugging iOS applications at runtime.

By setting breakpoints at different locations in our code, the debugger temporarily stops executing the app when it hits a breakpoint. When our app pauses at a breakpoint, we can either use Xcode's variable viewer or LLDB commands to inspect variables in the console.

To be able to do that, the LLDB debugger provides the following print commands:

Let's look at how to use each of them and what their differences are.

LLDB command v

The LLDB v command is an alias for the command frame variable. Xcode uses it to generate a summary in its variable viewer.

The v command returns only what is currently in memory. It outputs an LLDB-formatted description.

(lldb) v bird
(ExampleApp.Bird) bird = {
name = "kea"
conservationStatus = en
}

The command doesn’t evaluate expressions, for example it won't print a function call or a calculated variable.

(lldb) v bird.conservationStatus.title
error: "title" is not a member of "(ExampleApp.ConservationStatus) bird.conservationStatus"

For example, when trying to inspect the computed variable title of the ConservationStatus enum, the v command returns an error.

LLDB command p

The p command is an alias for the command expression. It outputs an LLDB-formatted description by using a data formatter just like the v command.

The difference is that it compiles code to evaluate the expression, so it can handle function calls and calculated variables.

(lldb) p bird
(ExampleApp.Bird) $R2 = {
name = "kea"
conservationStatus = en
}
(lldb) p bird.conservationStatus.title
(String) $R3 = "endangered"

LLDB command po

The po command is an alias for expression --object-description. Just like the p command, it evaluates the expression.

The difference is that instead of a LLDB-formatted description, it prints out the debugging description which is a textual representation of an instance of the type.

It's either a default system debug description or a custom one we provide by conforming to the CustomDebugStringConvertable protocol.

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

Conclusion

Since p and po commands compile code dynamically to evaluate expressions, it takes more time to evaluate the variable and log it to the console compared to the v command.

Usually, the time difference doesn't matter, so we can just use p or po since they provide more possibilities.

There are cases though where timing is important, for example when trying to reproduce a bug in concurrent code. In this case, we might need to inspect variables without pausing and disturbing the timing of the running app.

In those cases, we can configure the breakpoint to Automatically continue after evaluating actions to prevent pausing and to use the v command for logging to reduce timing issues.

SF Symbols availability.
Inspecting variables without pausing the app.

Newsletter

Like to support my work?

Say hi

Related tags

Articles with related topics

Latest articles and tips