Xcode调试技巧总结
前言
本来觉得调试是一件很简单的事情,但是看了很多介绍调试方法的文章,发现有些技巧并不知道,有必要对常用的Xcode调试技巧做一个总结,提高工作效率。
一、调试面板
上方:断点开关、继续执行、单步执行、单步步入、单步步过等命令;
左边:watch窗口,负责变量信息显示,如果想查看寄存器的内容,可以将左下角的Auto切换为All
右边:日志窗口,接受和显示程序日志,左下角可以选择All/Debugger/Target output
二、断点
1- 普通断点
找到下断点的代码行,可以通过下面3种方式下断点:
(1)导航栏:Debug->Breakpoint->Add Breakpoint at Current Line
(2)快捷键:command +
(3)鼠标:直接在编辑区域左边行号的地方左键
大部分情况普通断点就满足需求了,但是对于一些特殊的调试情况,还需要掌握一些其他类型的断点。
2- 条件断点
适用场景:
(1)一个函数重复多次被调用,但是只需要调试其中某一次的情况时;
(2)对于一些因为异常数据导致的bug调试也很实用;
下断点: 右键普通断点 -> Edit Breakpoint,条件断点和普通断点相比只是多了一个条件判断而已,和我们手动在断点代码加一个if条件判断效果一样,只有满足条件的情况才会断下来;
3- 符号断点
符号断点: 其实就是对一个特定的函数名下断点,这里的方法可以是OC方法或者C++函数名;
适用场景: 调试一些没有源码的模块时比较有用,比如调试一个第三方提供的Lib库,或者系统模块,可以给相应函数下断点,调试程序的运行流程,查看一些参数信息;
下断点 :断点Tab页 -> 点击下面+号 -> Symbolic Breakpoint
设置符号断点可以输入类名+函数名,也可以只输入函数名,xcode会自动匹配不同类中同名的方法进行断点,如下DJTPerson和DJTAnimal都有-(void)djt_run方法,会自动生成两个断点,一旦被调用就会命中断点:
4- 异常断点
适用场景: 异常断点用来调试程序抛出异常而导致退出,下个异常断点很快就能定位运行到那行代码出了问题;
下断点: 断点Tab -> 左下角+号 -> Exception Breakpoint
Exception Breakpoint也是可以编辑的,可以选择Exception类型,也可以选择在抛出异常或者捕获异常的时候断点等;
注:有的程序会使用异常来组织程序逻辑,比如微信扫一扫,所以如果Exception选了All,那么异常断点会频繁触发,所以这种情况可以只选择Objective-C异常。
下面是一个异常断点,在DJTPerson类中只有djt_run方法的声明没有实现,触发断点:
5- watch断点
顾名思义:watch断点就是当某个变量发生改变时触发的断点;
适用场景: watch断点对于要跟踪某个变量或者某个状态的变化时非常有用的,可以方便的跟踪到哪些地方改变了变量的值。
下断点: 在xcode的watch窗口 -> 右键需要watch的变量 -> watch "Xxx":
如例子中,当_name变量发生变化时调试器会自动断下来,同时输出变化信息:
6- 线程断点
线程断点: 线程断点适用在调试多线程代码的时候,一段代码可能会被多个线程同时执行,如果下普通断点,那么你会在不同线程之间切来切去,最后自己都迷糊了,这个时候可以使用多线程断点。
下断点: 调试区域右边控制台输出 -> breakpoint set –f 文件名 –l 行号 –t 线程id
下面例子在28行设置普通断点,就可以在控制台打印 thread-id,控制台输入:
thread info
获取当前线程id,在控制台通过命令行给32行设置线程断点:
breakpoint set -f ViewController.m -l 32 -t 0x331854
7- 断点后的Action
断点后的Action: 当断点被触发可以执行的一些操作;
下断点: 右键断点 -> Edit Breakpoint -> Add action
action类型很多,有调试命令、apple script、shell script等:
下边是在运行到断点后po一下person.name,直接打印了name的值:
如果觉得仅仅输出对象信息不够,还想加一些自己指定的内容,可以使用Log Message。
三、常用命令
1- p命令
p命令:查看基本数据类型的值
po命令:查看oc对象
简单查看一个变量或者OC对象的值在watch窗口完全可以满足,但是如果需要查看一个oc对象的属性,或者一个oc对象方法的返回值怎么办呢?p和po命令后面都可以接相应的表达式,如:
2- expr命令
expr命令:全称expression,可以在调试时动态修改变量的值,同时打印出结果。使用expr命令动态修改变量的值,可以在调试的时候覆盖一些异常路径,对调试异常处理的代码很有用。
3- call命令
call命令用来动态调用函数,可以在不增加代码不重新编译的情况下动态调用一个方法。下例动态将view1从父view移除:
4- image命令
image命令可以列出当前app中的所有模块,可以查找一个地址对应的代码位置,在调试越狱插件时,可以用image list命令查看越狱插件是否注入自己的App。当遇到crash时,查看线程栈只能看到栈帧的地址,使用image lookup -address 地址
命令可以方便的定位这个地址对应的代码行。
5- bt命令
bt命令可以查看线程的堆栈信息,该信息也可以在导航区的Debug Navigator看到;
bt:打印当前线程栈
bt all:打印所有线程栈
分割线:上边介绍了基本的调试技巧,下面是一些不同场景下的调试经验
四、多线程
场景:在调试的时候bug不出现,一旦关闭调试直接运行bug就出现:这种问题大部分是因为多线程bug,而调试影响了多线程的执行顺序。
技巧:这种问题可以在关键点输出log,之前介绍的断点action中的Log Message就派上用场,这样的好处是不需要在代码中添加冗余的log即可调试;在调试多线程问题时,合理使用线程断点和条件断点也是很有帮助的;
五、UI调试
1-控件信息
查看控件信息除了使用p和po命令,还可以使用expr命令修改控件属性,如内容、坐标、大小等,这样可以不重启程序看到界面的变化;
2-界面结构
查看界面结构:po [view recursiveDescription]
,该命令可以打印出view的所有子view的结构关系,对于调试界面层级关系很有用;
3-快速预览
xcode支持在调试时对变量进行快速预览,调试时将鼠标放在变量上,然后点击快速预览按钮即可看到控件的显示。
4-符号断点跟踪UI变化
对于一些系统控件的信息,如果发现最终显示和自己设置的不一样,可以使用符号断点,在一些设置函数下断点,这样就可以很清晰的看到是从哪里改变了这个属性的值。比如一个UIButton的title在显示的时候和设置的不一样,只需要符号断点设置setTitle就可以跟踪哪里改了值;
作者:D___
链接:https://juejin.cn/post/6950852311346315271