Xcode 调试技巧 --经常使用命令和断点

Xcode 中的调试技巧与咱们的平常开发息息相关,而这些调试技巧在咱们解决Bug时,经常有事半功倍的做用,常常会用到的有各类断点 和 命令。而这些调试技巧也常常会在面试中问到,因此不知道的就来看看吧。面试

调试主要观看区

调试命令

在上图中,右侧绿色区域就是Log 输出区,在 Log 输出区可使用一些命令,来辅助调试。express

那有哪些调试命令呢?bash

想要看全部的调试命令,能够在上图的右侧区域输入help,就会列出全部的调试命令。 本文就介绍几个使用频率比较高的,其余就查看后,自行了解吧。app

1. p 命令

-- ('expression --')  Evaluate an expression on the current thread.
                      Displays any returned value with LLDB's default formatting. 复制代码

p 命令是 print 命令的简写,使用p 命令能够查看基本数据类型的值,可是若是 使用 p 命令 查看的是对象,那么只会返回对象的指针地址。函数

p 命令后面除了能够接 变量、常量,还能够接 表达式。(❌可是不可使用宏❌)oop

2. po 命令

po 命令能够理解为打印对象。功能与 p 命令相似,因此也是能够打印 常量、变量,打印表达式返回的对象等。(❌也不能够打印宏❌)lua

p 和 po 使用范例

固然,这些打印功能,除了使用命令外,咱们也可使用左侧区域,点击变量右键---> print Description of “xxx”: spa

Paste_Image.png

固然还有其余的打印方法:线程

3.expr 命令

expr 是 expression 的简写, 使用expr 命令,可以在调试时,动态的执行赋值表达式,同时打印出结果。咱们能够在调试时,动态的修改变量的值,这在调试想要让应用执行异常路径(如执行某个else 状况)颇有用。3d

(lldb) p i 
(NSInteger) $16 = 1
(lldb) expression i = 5
(NSInteger) $17 = 5
(lldb) po i 
5
复制代码

4.call 命令

上面是动态修改变量的值, Xcode 还支持动态调用函数。在控制台执行该命令,能够在不修改代码,不从新编译的状况下,修改界面上的视图。 这里有一个动态将cell 的某个子视图移除的范例:

(lldb) po cell.contentView.subviews
<__NSArrayM 0x60800005f5f0>(
<UILabel: 0x7f91f4f18c90; frame = (5 5; 300 25); text = '2 - Drawing index is top ...'; userInteractionEnabled = NO; tag = 1; layer = <_UILabelLayer: 0x60800009ff40>>,
<UIImageView: 0x7f91f4d20050; frame = (105 20; 85 85); opaque = NO; userInteractionEnabled = NO; tag = 2; layer = <CALayer: 0x60000003ff60>>,
<UIImageView: 0x7f91f4f18f10; frame = (200 20; 85 85); opaque = NO; userInteractionEnabled = NO; tag = 3; layer = <CALayer: 0x608000039860>>
)

(lldb) call [label removeFromSuperview]
(lldb) po cell.contentView.subviews
<__NSArrayM 0x600000246de0>(
<UIImageView: 0x7f91f4d20050; frame = (105 20; 85 85); opaque = NO; userInteractionEnabled = NO; tag = 2; layer = <CALayer: 0x60000003ff60>>,
<UIImageView: 0x7f91f4f18f10; frame = (200 20; 85 85); opaque = NO; userInteractionEnabled = NO; tag = 3; layer = <CALayer: 0x608000039860>>
)
复制代码

5.bt命令

bt 命令 能够打印出线程的堆栈信息,该信息比左侧的Debug Navigator 看到的还要详细一些。

bt 命令是打印当前线程的堆栈信息

(lldb) bt 
* thread #1: tid = 0x27363, 0x000000010d204125 TestDemo`-[FifthViewController tableView:cellForRowAtIndexPath:](self=0x00007f91f4e153c0, _cmd="tableView:cellForRowAtIndexPath:", tableView=0x00007f91f5889600, indexPath=0xc000000000400016) + 2757 at FifthViewController.m:91, queue = 'com.apple.main-thread', stop reason = breakpoint 6.1
  * frame #0: 0x000000010d204125 TestDemo`-[FifthViewController tableView:cellForRowAtIndexPath:](self=0x00007f91f4e153c0, _cmd="tableView:cellForRowAtIndexPath:", tableView=0x00007f91f5889600, indexPath=0xc000000000400016) + 2757 at FifthViewController.m:91
    frame #1: 0x0000000111d0a7b5 UIKit`-[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 757
    frame #2: 0x0000000111d0aa13 UIKit`-[UITableView _createPreparedCellForGlobalRow:willDisplay:] + 74
    frame #3: 0x0000000111cde47d UIKit`-[UITableView _updateVisibleCellsNow:isRecursive:] + 3295
    frame #4: 0x0000000111d13d95 UIKit`-[UITableView _performWithCachedTraitCollection:] + 110
    frame #5: 0x0000000111cfa5ef UIKit`-[UITableView layoutSubviews] + 222
    frame #6: 0x0000000111c61f50 UIKit`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1237
    frame #7: 0x00000001117a5cc4 QuartzCore`-[CALayer layoutSublayers] + 146
    frame #8: 0x0000000111799788 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 366
    frame #9: 0x0000000111799606 QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 24
    frame #10: 0x0000000111727680 QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 280
    frame #11: 0x0000000111754767 QuartzCore`CA::Transaction::commit() + 475
    frame #12: 0x00000001117550d7 QuartzCore`CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 113
    frame #13: 0x0000000110743e17 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
    frame #14: 0x0000000110743d87 CoreFoundation`__CFRunLoopDoObservers + 391
    frame #15: 0x0000000110728b9e CoreFoundation`__CFRunLoopRun + 1198
    frame #16: 0x0000000110728494 CoreFoundation`CFRunLoopRunSpecific + 420
    frame #17: 0x0000000114390a6f GraphicsServices`GSEventRunModal + 161
    frame #18: 0x0000000111b9d964 UIKit`UIApplicationMain + 159
    frame #19: 0x000000010d21294f TestDemo`main(argc=1, argv=0x00007fff529fe620) + 111 at main.m:14
    frame #20: 0x000000011458a68d libdyld.dylib`start + 1
(lldb) 
复制代码

bt all 命令是打印全部线程的堆栈信息。打印出来的信息太多,就不展现了!

6.image 命令

image list 命令能够列出当前App中的全部module(这个module 在后面符号断点时有用到),能够查看某一个地址对应的代码位置。 除了 image list 还有 image addimage lookup等命令,能够自行查看。 当遇到crash 时,查看线程栈,只能看到栈帧的地址,使用 image lookup –address 地址 能够方便的定位到这个地址对应的代码行。

断点

Xcode 中的断点也是颇有学问的,有普通断点、条件断点、符号断点、异常断点等不少种。

1.普通断点

打一个普通断点,只须要找到对应的行,在代码左侧(行号上)点击一下便可。

2.条件断点

条件断点是一种颇有用的断点,特别是在for 循环中。若是咱们须要在i = 5 时添加断点,其余时候不加,那么就可使用条件断点。条件断点是在普通断点上 右键,选择 Edit Breakpoint...,再设置一个条件便可

编辑普通断点

添加条件

3.符号断点

符号断点就是 Symbolic Breakpoint,实际上是针对某一个特定函数的断点,能够是一个 OC函数,也能够是 C++函数。 添加的地方以下:

符号断点

符号断点条件
Symbol 栏 能够填 [类名 方法名]或者 方法名 ,module 也是选填项,它就是上面  image  命令中列出来的module。 例如 ,咱们若是只填一个viewDidLoad,那么就会在全部类(包括第三方库)的viewDidLoad 处打断点。

符号断点在调试一些没有源码的模块时比较有用,好比调试一个第三方提供的Lib库,或者系统的模块,能够在相应函数处下断点,能够大概调试清楚程序的运行流程,也能够在断点的时候查看到参数信息。

4.异常断点

若是程序运行就崩溃,咱们能够打一个异常断点,这样崩溃时就会触发断点,很容易定位到问题所在,也能看到更多的崩溃相关信息,如Log,函数调用栈。

异常断点

能够修改异常断点的条件

注意: 有的程序或者有的功能可能会使用异常来组织程序逻辑,好比调用AVAudioPlayer ,运行到 AVAudioPlayer 时,就会致使断点被触发。咱们能够修改 Exception 参数,或者取消掉异常断点来解决。

5.Watch 断点

当某个变量发生变化的时候会触发。 建立一个Watch断点:

Watch 断点

关于 Xcode 调试技巧中的 断点和命令就先这么多了,其余有用到的之后再补充。

相关文章
相关标签/搜索