What I’m going to introduce today, which is not about any functional development technology, but has a profound impact on the efficiency of development, is debugging technology.

What about debugging? For example, we use the print function to print at a specified location to locate values in variables of certain nodes:

Let the result = parseJSON (" [1, 2, 3] "); print(result); result = parseJSON("error"); print(result);Copy the code

I believe we all see similar code will not be unfamiliar, estimated for developers friends will be more or less in such a way to debug the program.

This approach has the advantage that we don’t have to think too much about it, and when we need to trace something, we can just output it and get debugging information. The downside of this is that every time we debug like this, we compile, run, write a new print statement, compile, run again. Repeated compilation, running will be more time consuming. And after debugging, it is easy to forget to delete debugging statements, resulting in many output statements left in the code. With the long-term progress of the project, this will cause a lot of interference to the debugging in the later stage of the project.

Also, when we want to debug this area again, we have to write the output statements again. Sometimes for a slightly more complex debugging scenario, print output is not a good way to deal with.

The solution to these problems is debugging technology. Debuggers are available in almost every modern development environment, so iOS development is no exception. The Xcode environment provides the corresponding debugging tool for us is LLDB.

New LLDB

LLDB is the debugging tool that XCode provides for us. So with all that said, what exactly is a debugging tool? Debuggers may sound unfamiliar to you, but breakpoints should sound familiar. Let’s also take the code we just mentioned as an example:

We created a breakpoint by clicking on line 23 to the left, and when we run the application again, the breakpoint will be intercepted:

And in the command line area of Xcode, the (LLDB) prompt is displayed.

Basic debugging operations

Back to the original question, how can we get the value of result without using print? This is the breakpoint debugging mechanism we are going to discuss. Let’s take a look at some buttons in the debug area at the bottom of XCode:

  • The first button is “continue”, which will allow the program to resume from the breakpoint and continue, and when we click this button, the application will resume normal operation.
  • The second button is “Step Over,” which means “Step Over,” and every time you press this button, the program takes a Step down from where we started.
  • The third button is “Step In”, which means that if our current breakpoint is on a function call, the breakpoint will continue to go inside the function for debugging.
  • The fourth button is “Step Out”, which means that if we are in a function, it will Step Out of the current function and go back to where the function was called.

Well.. You have no idea what you’re talking about

Okay, let’s go back to our original requirement. Our breakpoint now stops in the statement that assigned the result variable. The statement where the breakpoint is has not been executed yet, so we need to hit the Step Over button (the second of the four buttons we just listed). Let the program execute a line of code.

After executing this line of code, our result variable is assigned. So the question is, how do we get the contents of the result variable?

Remember from our LLDB command line, we used a command called Po to fetch this variable:

Now we use the LLDB command to achieve the same effect as the print statement and get the value of the result variable. So, what’s the advantage of doing this? Why does this feel more troublesome than using print directly?

Here’s why.

LLDB discovery Tour

LLDB provides many convenient commands. You can enter the help command in the LLDB command line to see the help information of these commands:

Debugger commands:

  apropos           -- Find a list of debugger commands related to a particular
                       word/subject.
  breakpoint        -- A set of commands for operating on breakpoints. Also see
                       _regexp-break.
  command           -- A set of commands for managing or customizing the
                       debugger commands.
  disassemble       -- Disassemble bytes in the current function, or elsewhere
                       in the executable program as specified by the user.
  expression        -- Evaluate an expression (ObjC++ or Swift) in the current
                       program context, using user defined variables and
                       variables currently in scope.
  frame             -- A set of commands for operating on the current thread's
                       frames.

  ...............Copy the code

Here we see a list of LLDB commands. To get more detailed help on a command, we can type the name of the help command. For example, help expression:

help expression
     Evaluate an expression (ObjC++ or Swift) in the current program context,
     using user defined variables and variables currently in scope.  This
     command takes 'raw' input (no need to quote stuff).

Syntax: expression  -- 

Command Options Usage:
  expression [-AFLORTg] [-f ] [-G ] [-l ] [-a ] [-i ] [-t ] [-u ] [-v[]] [-d ] [-S ] [-D ] [-P ] [-Y[]] [-V ] -- 
  expression [-AFLORTg] [-l ] [-a ] [-i ] [-t ] [-u ] [-d ] [-S ] [-D ] [-P ] [-Y[]] [-V ] -- Copy the code

That gives you an introduction to the expression command.

So much for the basic situation, let’s put it into practice and experience the power of LLDB.

Let’s look at a more powerful command, expression. Let’s look at its description:

Evaluate an expression (ObjC++ or Swift) in the current program context,

using user defined variables and variables currently in scope. This

command takes ‘raw’ input (no need to quote stuff).

In the current program environment, any expression can be executed and existing variables can be defined and manipulated.

How about, let me be more specific, with LLDB we can not only print the value of a variable at a breakpoint, we can also modify or even redefine the value of a variable.

Let’s go ahead and modify our program:

Var result = parseJSON (" [1, 2, 3] "); if result? .count == 0 { print("No Data"); }else{ print(result); }Copy the code

Here we judge the result variable and output it separately. Let’s set the breakpoint on the first statement and run the program. At the breakpoint we use the Po command to print the value of result.

In this case, the value of result should be the parsed JSON array. So after we resume execution, the next if judgment goes to the second branch, which prints the contents of result.

So what if we run this command at the breakpoint:

e result = []Copy the code

Here we change the value of result to an empty array, and then we continue the program, and you see that the following if judgment takes the first branch, which means that the change we made to the variable at the breakpoint is valid for the global program.

How this ability is our previous debugging method can not achieve it ~

The e command is the abbreviation of the expression command. For details, see the LLDB help command.

Control flow shortcut command

As we continue to explore, remember the control flow buttons we mentioned earlier, namely this image:

On the LLDB command line, there are commands for each process control button.

  • nCommand, stand forStep OverOperation.
  • sCommand, stand forStep IntoOperation.
  • finishCommand, stand forStep OutOperation.
  • cCommand to perform operations on behalf of the recovery program.

Let’s use this program as an example, this time we use the control flow command to do the operation:

We run the program, and when breakpoints are detected, enter the LLDB commands in the following order:

s
n
n
c
Copy the code

The first s we type will step into the parseJSON function call, and then the breakpoint will go into the parseJSON function. We then enter the n command, and since there is only one return statement in parseJSON, the control flow will jump out of the parseJSON function body and return to the beginning. We then type the n command again, which assigns the result of parseJSON to result. Finally, we press c to resume the execution of the program. The subsequent if judgment outputs the content according to the corresponding condition.

How about that? It’s easier to operate, we don’t have to point and point with the mouse, we can completely use the keyboard to type commands to complete the control flow operation.

In addition, there is a more useful control flow statement, Thread Return. This command is interesting in that it can not only make the current function return, but also modify the return value of the current function arbitrarily, regardless of the parameters passed in. Let’s say we have a function like this:

func add(a:Int, b:Int) -> Int {

    return a + b;

}Copy the code

If we execute Thread Return 3 when the breakpoint enters the function body, the function exits regardless of the two arguments passed in and returns the specified value of 3.

Well.. There’s something magical about this

Breakpoint creation command

Instead of creating breakpoints by clicking on the left side of the line of code, we can use LLDB to create breakpoints, for example, to create a breakpoint like ours:

We can type a command like this:

(lldb) breakpoint set -f ViewController.swift -l 28
Breakpoint 2: where = Example`Example.ViewController.viewDidLoad (Example.ViewController)() -> () + 478 at ViewController.swift:29, address= 0x000000010f74f61eCopy the code

The command is followed by a line of output telling us that the breakpoint was created successfully and showing basic information such as the location of the new breakpoint.

This command can also be abbreviated:

b ViewController.swift:28
Copy the code

We can also set the breakpoint directly to the function, assuming we have a function like this:

func add(a:Int, b:Int) -> Int {

    return a + b;

}Copy the code

We can also set breakpoints like this:

b add
Copy the code

This sets the breakpoint at the start of the add call.

We can set symbolic breakpoints like this:

b -[NSArray objectAtIndex:]
Copy the code

This breakpoint sets all calls to NSArray’s objectAtIndex method as a breakpoint. This includes calls to it by our developers, as well as calls to it within the system framework. Symbolic breakpoints are a great tool for debugging, tracking down problems with code in the system libraries we reference.

We can also set fire conditions for breakpoints that have already been created:

The condition we set above means that the breakpoint is fired only if the count property of result is equal to 0.

Do you think THE LLDB is a bit interesting?

This long list has introduced a lot of basic LLDB content to you. I believe you have an overall understanding of THE LLDB.

Began to adventure

So now let’s do something more interesting with LLDB.

Let’s start by creating a sample project:

Select Single View Application for the project type

Then click Next and select Swift for Language in the project information:

Click Next and the project location option appears. Select one of your own. Next we drag and drop a Button from main. storyboard into the upper right corner:

We then hold down the Option key and click on the ViewController.swift file to open the secondary screen next to the design screen. Once opened, we hold down the Control key and drag the button we just created into the code view:

Then release the mouse button and we will see a pop-up menu:

We select the Connection type as Action, Name as buttonClicked, and nothing else, and click the Connect button. This completes the creation of the button event.

Next, we run the application and see something like this:

With everything in place, we can now expand our LLDB method ~

We can also open the LLDB command line without using a breakpoint. Before we run the program, let’s look at the debug button:

Note that there is also a pause button to the right of the blue breakpoint switch button. Just click the pause button to enter the LLDB command line debugging state. Because LLDB resides in the background during Xcode execution, we can actually start the LLDB command line at any time.

After opening the LLDB command line, we can type this command to print out the current view hierarchy:

(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription] ; layer = > | > | | > | | | > | | <_UILayoutGuide: 0x7ffcd2c6fae0; frame = (0 0; 0 to 20); hidden = YES; layer = > | | <_UILayoutGuide: 0x7ffcd2c70740; frame = (0 667; 0, 0); hidden = YES; layer = >Copy the code

If you look closely, each view has a hexadecimal string that represents the view ID, such as this:

UIView: 0x7ffcd2c6dc10

This ID is very powerful. With this ID, we can use this command to get a reference to the view:

(lldb) e id $view = (id) 0x7fbd71432590
Copy the code

For a quick explanation, using the expression command (here using the abbreviation e), we get the View reference with the View ID value and save it to the $View variable.

Once we have the reference, we can do a lot of things with the view. For example, we can change the view’s background color at runtime:

(lldb) e (void) [$view setBackgroundColor:[UIColor redColor]]Copy the code

Of course, after we run this command, the interface will not immediately react, we need to call this command to refresh:

(lldb) e (void)[CATransaction flush]
Copy the code

So, if we look at the program we’re running, the background color on the main screen has changed to red.

We can even use it to find events added to certain controls. We find the ID of UIBUtton that we added ourselves:

UIButton: 0x7ffcd2c6dfc0

Then run the following command:

(lldb) e id $button = (id) 0x7ffcd2c6dfc0

(lldb) po [$button  allTargets]
{(
    
)}Copy the code

After we get the reference to UIButton, we print its allTargets property to get the address of the UIButton’s event target object. Then we use the target address to get its action property:

(lldb) po [$button actionsForTarget:(id)0x7feff2d67330 forControlEvent:0]
<__NSArrayM 0x7feff2c22350>(
buttonClicked:
)Copy the code

So now we have the method name for this button. Then we can set breakpoints on this method, or replace the method implementation with the run-time capabilities of the LLDB, etc. In summary, LLDB is a very powerful and efficient tool for debugging applications. This is just the tip of the iceberg. For more information, you can use the help command to dig deeper. I believe you can use your wisdom and wisdom to discover more of its strengths.

A little bit of extension, about Chisel

And finally, I’ll give you an extension. The LLDB command system itself is very robust, and it also supports Python script extensions, which makes it very extensible and allows you to extend your own scripts to suit your needs.

Chisel is a good example of an LLDB extension. Chisel is an open source Python extension to LLDB developed by the Facebook team, which provides a more convenient interface to operate on top of the LLDB command.

For example, if we want to print the current view hierarchy, if we use LLDB native commands, we need to look like this:

(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
Copy the code

Chisel gives us a cleaner interface:

(lldb) pviews
Copy the code

Similarly, this command refreshes the display:

(lldb) e (void)[CATransaction flush]
Copy the code

Chisel also provides us with a simple interface:

(lldb) caflush
Copy the code

Here is a brief introduction. For more information about Chisel, you can see his home page: github.com/facebook/ch…

The command line system of LLDB itself, as well as its ability to extend, has become a powerful tool to improve our development efficiency. The correct use of debugging tools will help us solve more problems quickly.

Now, start exploring the LLDB command line with the help command, where you’ll find even more treasures.

This site articles are original content, if you need to reprint, please indicate the source, thank you.