What is LLDB?
LLDB is Xcode's integrated low-level debugger, essential for examining and debugging applications across Apple's platforms. It enables developers to set breakpoints, inspect variables, and navigate through code execution to diagnose and resolve issues efficiently. With support for both graphical and command-line interfaces, LLDB streamlines debugging workflows from simple variable checks to complex debugging tasks
How to Interact with LLDB?
LLDB allows different ways to interact with the application. such as po, p and v
Let's take a look at below code Snippet.
import Foundation
enum ClassName {
case c1;
case c2;
case c3;
}
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
func setName(name: String) {
self.name = name
}
}
class Student: Person {
var className: ClassName
init(name: String, age: Int, className: ClassName) {
self.className = className
super.init(name: name, age: age)
}
}
let student = Student(name: "vinay", age: 26, className: .c1)
struct StudentStruct {
let name: String
let age: Int
}
let studentStruct = StudentStruct(name: "vinayStruct", age: 26)
What is po?
In Xcode, po stands for "print object," which is used to print the description of an object during debugging. When you use po followed by a variable name in the debugger console, it displays detailed information about that object.
For example, if you want to print the variable student
, executing po student
in the debugger console will display the object's description or address.
po student
For a variable like studentStruct
, which is a struct type and already provides a default debugDescription method, po
calls this method to show the detailed description of the object.
po studentStruct
If you confirm the Student class to CustomDebugStringConvertible
and override its debugDescription
method as shown below:
class Student: Person, CustomDebugStringConvertible {
var debugDescription: String {
return "Student(\(self.name), \(self.age))"
}
var className: ClassName
init(name: String, age: Int, className: ClassName) {
self.className = className
super.init(name: name, age: age)
}
}
let student = Student(name: "vinay", age: 26, className: .c1)
By implementing CustomDebugStringConvertible, you provide a custom implementation for the debugDescription property. When you use po student in the debugger console in Xcode, it automatically uses this custom debugDescription method to print a formatted description of the student object, incorporating its name, age, and any other relevant properties or state.
This demonstration clearly illustrates that po is utilized to display the object's description while debugging.
That's all
po
does? no, It can do more. you can even call the string methods on thename
variable of thestudent
po student.name.uppercased()
In general, It can evaluate any arbitrary expression.
In fact, po is alias for the command
expression
.
example:
expression --object-description -- student
you can even create your own
po
commands
command alias my_po expression --object-description --
my_po student
To unalias the created one use below command
command unalias my_po
What is p?
This is a second way of printing a variable in LLDB. You can think it as print without object description.
p student
- The representation differs slightly from the one provided by
po
. But it is equivalent as it gives the same information about the instance variables -
p
is an alias for the commandexpression
but without--object-description
flag - This
p
gives an incremental name to the result like $R0, $R1 etc. -
p
uses DataFormatters to print the information. Anyway, we are not covering DataFormatters in this tutorial.
If you use
p
in the latest Xcode version variables ex: $R0name
is not showing. But it is showing if you're using theexpression
command. I am not sure why.
What is v?
To understand v, declare a variable studentWithBaseType
as Person
, not Student
.
let studentWithBaseType: Person = Student(name: "vinaywithbasetype", age: 26, className: .c1)
Since studentWithBaseType
is of type Person, you cannot read properties specific to Student without casting it first, like accessing className on studentWithBaseType, which Swift's compiler does not allow. To read it, you must cast it:
(studentWithBaseType as! Student).className
Similarly, LLDB dynamically resolves variable types only in expressions. Attempting to access properties directly on the result in LLDB results in an error, as shown below:
But By using v
you can read the variables of the actual type Student in the LLDB console
v studentWithBaseType.className
Differences
po (print object):
Use: Print detailed object descriptions during debugging, especially useful for custom debug descriptions (CustomDebugStringConvertible).
When to Use: Use po when you need a formatted and detailed view of an object's state, including custom debug descriptions.
p (print):
Use: Provides basic information about variables and objects without the detailed object description.
When to Use: Use p for quick checks on variable values or when detailed object description isn't necessary, providing a concise view of the variable's state.
v (dynamic resolution):
Use: Used for dynamically resolving types in LLDB expressions, essential when dealing with base types containing more specific class types.
When to Use: Use v to dynamically resolve and access properties specific to a subclass or more detailed class type within LLDB expressions.
Conclusion:
LLDB in Xcode offers powerful tools (po, p, v) for developers to debug applications comprehensively, from simple variable checks to complex debugging tasks, improving overall code quality and performance
for in-depth understanding refer this Apple World Wide Developer Conference - WWDC2019 Resouce
Top comments (0)