在平时开发中,我们可能需要调试某些东西,比如查看给服务器发请求时传过去的参数,如果不适用LLDB的话我们用的最多的就是通过NSLog方式去打印,但现在我们可以精简这个步骤,那就是使用LLDB调试命令.
Xcode从4.0开始编译器开始改用LLVM,相应的调试器也从gdb改为LLDB。而从 Xcode5.0开始所有工程也被自动设置为使用LLDB。
使用:
首先,我们需要先设置一个断点;
当程序执行到断电的时候就会弹出LLDB调试面板:
这个时候我们可以在这里输入对应命令行回车查看或更改参数
po 指令可以用来打印一些对象或者字符串
出了打印参数信息外,还可以动态更改属性值及其他功能,具体可以参照参考资料
print、p:打印
po:打印对象
e:修改变量的值
n:运行至下一行
c:运行至下个断点处(下面没有断点的话则继续运行程序)
call:调用。其实上述p/po后接表达式(expression)也有调用的功能,一般只在不需要显式输出,或是无返回值时使用call,用于动态调试插入调用代码。
例如可以在viewDidLoad:里面设置断点,然后在程序中断的时候输入以下命令:
call [self.view setBackgroundColor:[UIColor redColor]];
image:可用于寻址(这个命令和图片没有任何关系) 这个功能很强大了 比如我们经常项目出现crash 控制台上就会打印下面的信息 这个时候我们可以根据这个信息来准确找到闪退位置(虽然闪退原因头部写的很清楚了 但是假如这个文件有很多setValueForKey方法呢,我们一个个找起来太费劲 我们就可以通过image来准确定位)
首先我们在上面信息中查找我们运行的程序的内存地址:也就是第4个 KVODemo对应的内存地址 0x00000001089faf99
然后在LLDB控制器面板输入命令:image lookup --address 0x00000001089faf99 然后回车
就看到打印信息:
Address: KVODemo[0x0000000100001f99] (KVODemo.__TEXT.__text + 1113)
Summary: KVODemo`-[ViewController touchesBegan:withEvent:] + 265 at ViewController.m:126:5
Summary就会显示哪个文件多少行 在ViewController.m文件的126行
bt(backtrace),打印当前调用堆栈(crash堆栈),“bt all”可打印所有thread的堆栈(相当于command+6的Debug Session Navigation)。
首先介绍一下什么叫调用堆栈:假设我们有几个函数,分别是function1,function2,function3,funtion4,且function1调用function2,function2调用function3,function3调用function4。在function4运行过程中,我们可以从线程当前堆栈中了解到调用他的那几个函数分别是谁。把函数的顺序关系看,function4、function3、function2、function1呈现出一种“堆栈”的特征,最后被调用的函数出现在最上方。因此称呼这种关系为调用堆栈(call stack)。
2. 作用
“调用堆栈”窗口可以查看当前堆栈上的函数或过程调用。
“调用堆栈”窗口显示每个函数的名称和编写它所用的编程语言。函数或过程名可能伴随有可选信息,如模块名、行号、字节偏移量以及参数的名称、类型和值。 可以打开或关闭这些可选信息的显示。
一个黄色箭头标识执行指针当前所位于的堆栈帧。 默认情况下,该帧的信息显示在源、“反汇编”、“局部变量”、“监视”和“自动”窗口中。 如果想将上下文更改为堆栈上的另一个帧,可以在“调用堆栈”窗口中执行相应的操作。
当调试符号对部分调用堆栈不可用时,“调用堆栈”窗口也许就不能显示那部分堆栈的正确信息。
常用的场景:
当故障发生时,如果程序被中断,我们基本上只可以看到最后出错的函数。
利用调用堆栈,我们可以知道当出错函数被谁调用的时候出错。这样一层层的看上去,有时可以猜测出错误的原因。
读取对象的内存地址:memory red ↓↓ 关于内存地址和对象本质的问题在底层文章中有所介绍
memory read/数量格式字节数 读取对象/或某个具体的内存地址
(lldb) memory read/3xw ani
0x6000010893a0: 0x07ff3318 0x00000001 0x00000001
数量是指读取从该对象起始地址或该内存地址起始地址后多长的地址
格式
x是16进制,f是浮点,d是10进制
字节大小
b:byte 1字节,h:half word 2字节
w:word 4字节,g:giant word 8字节
修改内存中的值
memory write 内存地址 数值↓↓↓
当我们读取到对象的地址值是 就可以动态修改内存地址所指向数据的值 比如上图中age属性的地址值是**0x6000010893a8开始后的四个字节 **
(lldb)memory write 0x6000010893a8 5
我们再打印age的属性值时 就变成了5
(lldb) p ani.age
(int) $18 = 5
(lldb)
这个值的修改是真实有效的 并不是只存在于lldb面板中 在程序中都是有效的
frame:(这个frame并不是用来设置布局的frma 而是[帧] 也就是我们通过bt命令打印出来的调用堆栈)用于选择和检查当前线程堆栈帧的命令。
frame variable
平时Debug的时候我们经常做的事就是查看变量的值,通过frame variable命令,可以打印出当前frame[也就是断点所在的方法]的所有变量
(lldb) frame variable
(ViewController *) self = 0x00007fc263c10930
(SEL) _cmd = "viewDidLoad"
(PersonClass *) person1 = 0x00006000012c8b10
(AnimalClass *) ani = 0x00006000010893a0
(PersonClass *) person2 = 0x00006000012cc4a0
frame info: 查看当前frame[当前断点所在的方法]的信息
(lldb) frame info
frame #0: 0x0000000107fefdfa KVODemo`-[ViewController viewDidLoad](self=0x00007fc263c10930, _cmd="viewDidLoad") at ViewController.m:32:20
frame select: 选择某个frame(不常用) 这里select 后面跟的数字就是通过bt打印出来的当前调用堆栈对应的frame数字
(lldb) frame select 1
frame #1: 0x000000010ba8f43b UIKitCore`-[UIViewController loadViewIfRequired] + 1183
UIKitCore`-[UIViewController loadViewIfRequired]:
-> 0x10ba8f43b <+1183>: movl (%r14), %eax
0x10ba8f43e <+1186>: testl %eax, %eax
0x10ba8f440 <+1188>: je 0x10ba8f786 ; <+2026>
0x10ba8f446 <+1194>: cmpl $0x70000, %eax ; imm = 0x70000
(lldb) frame select 8
frame #8: 0x0000000114ef86d6 UIKitCore`__111-[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:]_block_invoke + 904
UIKitCore`__111-[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:]_block_invoke:
-> 0x114ef86d6 <+904>: movq (%rbx), %rdi
0x114ef86d9 <+907>: movq 0x1442aa8(%rip), %rsi ; "_calledRunWithMainScene"
0x114ef86e0 <+914>: callq *%r14
0x114ef86e3 <+917>: movq %r12, %rdi
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x000000010f602d58 KVODemo`-[ViewController viewDidLoad](self=0x00007faca36083e0, _cmd="viewDidLoad") at ViewController.m:25:25
frame #1: 0x00000001150b243b UIKitCore`-[UIViewController loadViewIfRequired] + 1183
frame #2: 0x00000001150b2868 UIKitCore`-[UIViewController view] + 27
frame #3: 0x00000001156eac33 UIKitCore`-[UIWindow addRootViewControllerViewIfPossible] + 122
frame #4: 0x00000001156eb327 UIKitCore`-[UIWindow _setHidden:forced:] + 289
frame #5: 0x00000001156fdf86 UIKitCore`-[UIWindow makeKeyAndVisible] + 42
frame #6: 0x00000001156adf1c UIKitCore`-[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4555
frame #7: 0x00000001156b30c6 UIKitCore`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1617
* frame #8: 0x0000000114ef86d6 UIKitCore`__111-[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:]_block_invoke + 904
frame #9: 0x0000000114f00fce UIKitCore`+[_UICanvas _enqueuePostSettingUpdateTransactionBlock:] + 153
frame #10: 0x0000000114ef82ec UIKitCore`-[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:] + 236
frame #11: 0x0000000114ef8c48 UIKitCore`-[__UICanvasLifecycleMonitor_Compatability activateEventsOnly:withContext:completion:] + 1091
frame #12: 0x0000000114ef6fba UIKitCore`__82-[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:]_block_invoke + 782
frame #13: 0x0000000114ef6c71 UIKitCore`-[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:] + 433
frame #14: 0x0000000114efb9b6 UIKitCore`__125-[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:]_block_invoke + 576
frame #15: 0x0000000114efc610 UIKitCore`_performActionsWithDelayForTransitionContext + 100
frame #16: 0x0000000114efb71d UIKitCore`-[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:] + 223
frame #17: 0x0000000114f006d0 UIKitCore`-[_UICanvas scene:didUpdateWithDiff:transitionContext:completion:] + 392
frame #18: 0x00000001156b19a8 UIKitCore`-[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 514
frame #19: 0x0000000115268dfa UIKitCore`-[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 361
frame #20: 0x000000011bf9f125 FrontBoardServices`-[FBSSceneImpl _didCreateWithTransitionContext:completion:] + 448
frame #21: 0x000000011bfa8ed6 FrontBoardServices`__56-[FBSWorkspace client:handleCreateScene:withCompletion:]_block_invoke_2 + 283
frame #22: 0x000000011bfa8700 FrontBoardServices`__40-[FBSWorkspace _performDelegateCallOut:]_block_invoke + 53
frame #23: 0x00000001121a0db5 libdispatch.dylib`_dispatch_client_callout + 8
frame #24: 0x00000001121a42ba libdispatch.dylib`_dispatch_block_invoke_direct + 300
frame #25: 0x000000011bfda146 FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
frame #26: 0x000000011bfd9dfe FrontBoardServices`-[FBSSerialQueue _performNext] + 451
frame #27: 0x000000011bfda393 FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 42
frame #28: 0x000000011089dbe1 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #29: 0x000000011089d463 CoreFoundation`__CFRunLoopDoSources0 + 243
frame #30: 0x0000000110897b1f CoreFoundation`__CFRunLoopRun + 1231
frame #31: 0x0000000110897302 CoreFoundation`CFRunLoopRunSpecific + 626
frame #32: 0x0000000118ef82fe GraphicsServices`GSEventRunModal + 65
frame #33: 0x00000001156b4ba2 UIKitCore`UIApplicationMain + 140
frame #34: 0x000000010f603440 KVODemo`main(argc=1, argv=0x00007ffee05fbf08) at main.m:14:16
frame #35: 0x0000000112215541 libdyld.dylib`start + 1
frame #36: 0x0000000112215541 libdyld.dylib`start + 1
watchpoint:观察变量或者属性 这是一个非常有用的东西,我们经常遇到,某一个变量,不知道什么时候值被改掉了,就可以使用这个东西去定位:
watchpoint set variable self->_person1->_age
//注意 不能使用点语法
设置成功后会打印↓↓
(lldb) watchpoint set variable self->_person1->_age
Watchpoint created: Watchpoint 1: addr = 0x600001dbc948 size = 4 state = enabled type = w
watchpoint spec = 'self->_person1->_age'
new value: 1
这样当self.person1.age的值发生改变时就对自动断点 打印一下信息↓↓
Watchpoint 1 hit:
old value: 1
new value: 123
这个时候我们可以在左侧调用堆栈中找到调用的方法
也可以继续在控制器敲入bt:查看当前调用堆栈 找到对应的方法↓↓
这里只是介绍一些常用的 我们也可以在lldb调试面板中通过help指令来学习更多的功能↓↓