Today I am going to write about the big advanced debugging feature of LLDB. I am calling it advanced because I myself didn't know about it for a long time. It allows you to add a symbolic breakpoint which will halt the execution at a specific point so that you can backtrack and start investigating into where this method call was triggered.
If the introduction is not making sense, let me explain with the use-case. Suppose somewhere in your code, a method
dismissViewController which dismisses the
UIViewController gets called. Now there could be hundreds of
dismissViewController methods in your codebase. But how do you know which part of the code is actually calling this
dismissViewController method? Answer to this question is adding Symbolic Breakpoints.
Once you add a symbolic breakpoint to break at
dismissViewController method, the program execution will halt at it and traversing back in the stack trace will let you find the line which is responsible for dismissing the view controller.
Referring to the above screenshot, our program flow has stopped at the call of
dismissViewController. If we trace it back, we know that the method
viewDidAppear in class
Sample is responsible for dismissing this view controller.
Let's take a look at examples of different symbolic breakpoints which could be added during program execution,
- Symbolic breakpoint when the value is assigned to the object property
For example, you have a
UITextView object and you want to know the place where certain properties such as
translatesAutoresizingMaskIntoConstraints are assigned to them. You can click on Breakpoint Navigator and click on
+ symbol at the bottom and select
Symbolic Breakpoint as an option.
Now depending on which condition you want to break at, you can add the following values next to `Symbol` field
|view (On UIViewController)||-[UIViewController setView:]|
Now, you can ask - This is fine for putting a break on simple variables. But what about when property names follow names such as
Fortunately, it is very similar to what we did earlier. All you need to do it get rid of the prefix
is and simply apply the same logic as earlier.
2. Symbolic breakpoint when
UIViewController is presented
When it comes to stopping the program flow when the arbitrary place in your project prepares to present the view controller, you can do so by adding the following symbol to your symbolic breakpoint
3. Symbolic breakpoint when
UIViewController is dismissed
UIViewController is dismissed, it can be caught by adding the following symbol
4. Symbolic breakpoint when
UIViewController is pushed on the navigation stack
Similar symbol when
UIViewController instance is pushed on the navigation controller,
5. Symbolic breakpoint when you
UIViewController is popped from the navigation stack
And when current
UIViewController instance is popped from the navigation stack
6. Symbolic breakpoint when
UIViewController's lifecycle methods are called
This is a one-off trick to catch execution of any of the lifecycle methods associated with
UIViewController such as
viewWillAppear and so on. For example, you can stop the execution when the
viewDidLoad method gets called using the following symbol,
You may apply similar logic to break at other methods.
7. Symbolic breakpoint when the
view is added to its
Want to detect when the
UIView adds another
UIView instance as its
subView? Simply add the following symbol,
In the end, let's summarize the list of all the symbolic breakpoints that can be created with views and viewControllers to act as a handy list for any debug session,
|View Controller presented||-[UIViewController presentViewController:animated:completion:]|
|View Controller dismissed||-[UIViewController dismissViewControllerAnimated:completion:]|
|View Controller pushed on nav controller||-[UINavigationController pushViewController:animated:]|
|View Controller popped from nav controller||-[UINavigationController popViewControllerAnimated:]|
|viewDidLoad lifecycle method got called||-[UIViewController viewDidLoad]|
|viewDidAppear lifecycle method got called||-[UIViewController viewDidAppear:]|
|Added view as a subview to another view||-[UIView addSubview:]|
This is all for now. From my experience, I could only create symbolic breakpoints for classes and methods from existing iOS frameworks, and not for my own code. If you know the workaround for it, I would love to hear more.
If you are debugging excessively convoluted code and cannot for sure say where the particular caller is calling from, feel free to use this blog post as a cheat sheet. Any more questions? Feel free to comment, or hit me up on the Twitter!