Bugs,理论上是永远存在的,很多同学比较憎恶。何为bug?——小强是也,百打不死,它还和颜悦色,只时不时的作弄你一下。我倒觉得既然早晚得面对,与其憎恶还不如喜欢,“与之斗,其乐无穷”。
然而,日前帮“某生”调试了几个Bugs,了解到为什么其憎恶bug到极点,因为他不知道怎么跟bugs斗。因此,根据个人(有限的)经验整理一些常用的调试方法,聊做入门之参考。
“工欲善其事,必先利其器。”《论语·卫灵公》
Nori是在XCode环境下做iOS开发的,以下内容仅供XCode用户参考,如熟练掌握XCode调试的请走后门,不送……
XCode本身提供了大量的调试功能和工具,Nori比较懒只看了XCode文档的一部分,如欲成大神,自己回去把XCode的官方文档从头到尾看一遍。废话结束,开始正题。
XCode常用的Bugs调试手段如下:
用眼睛从头到尾看
这是Nori在09年刚入行时候的“调试”方法,此法本不提也罢,列出来是为了提醒大家该摒弃此法,效率太低。
强大的NSLog
NSLog()基本上是iOS开发中最常用的方法,新手第一个学习的函数估计就是他了。其定义如下:
void NSLog ( NSString *format, ... )
最简单莫过于NSLog(@"Hello World");
此项本不必多说,只说说其*format和一些特殊的用法。
NSLog()的format
format格式众多,不一一列举在此只罗列常用几种:
format | 含义 |
---|---|
%@ | 对象 |
%p | 指针 |
%d, %i | 整型 |
%u | 无符整型 |
%x, %X | 二进制整型 |
%o | 八进制整型 |
%lld | 64位长整型(long long) |
%f | 浮点型 |
%lf | 64位双字浮点型 |
%e | 浮点/双字 (科学计数法) |
%s | C字符串 |
%c | 字符 |
%C | unichar |
NSLog日志在控制台着色
关于日志的框架很多,只介绍Nori常用的一个简单着色的XCode插件——XcodeColors。
安装之后,在添加以下宏定义为XCodecolorHelper.h,然后全局import,更多用法自查。
效果如图:
利用私有API输出对象的更多信息
在一些时候,我们需要打印更多关于对象的信息,通常我们会自己重写-description或者-debugDescription方法来解决。自从知道了iOS7之后NSObject基类有了几个私有方法之后,一切都改变了。
调试时,将以上内容添加到工程。然后调用即可。
例如:
运行结果太长不全贴了,只贴_ivarDescription和_shortMethodDescription结果。
如何,有没有看到全裸美女的感觉呢?
更强的断点(Breakpoint)
断点(Breakpoint)是调试工程最常用,也是最传统的方法了,其实XCode断点调试这方面Nori不太敢写,因为了解不够全面,没能耐着性子把文档看完,但是本着敝帚也得拿出来显摆的原则,写写自己知道的或许能帮助一下哪些还不知道的。
在指定行添加/删除断点
调试工具栏
- 断点调试开关,添加断点时自动打开(图为打开),如果关闭XCode将disable所有断点;
- 运行时暂停,进入调试状态;
- 单步执行,点击一次执行一行,其他组合键ctrl+click、ctrl+shift+click,分别对应
指令
和线程
。 - 进入执行,如果执行到方法调用,点击即可进入该方法内部,配合5进行调试,其他组合键ctrl+click、ctrl+shift+click,同样分别对应
指令
和线程
。 - 当执行到方法内部,点击直接完成当前方法执行。
条件断点/约束断点(Conditional Breakpoints)
条件断点/约束断点(Conditional Breakpoints),即在满足指定条件下触发的断点。
- 添加断点;
- 在断点上右键,Edit Breakpoint…;
- 在Condition框中添加条件。
异常断点(Exceptions Breakpoint)
异常断点(Exceptions Breakpoint),当运行时有异常抛出时自动break,进入调试状态。因为是抛异常,所以貌似没法用来查找过度释放所导致的Crash。
- 在XCode左侧导航里切换到断点导航,⌘+7;
- 单机“+”, Add Exceptions Breakpoint;
- 然后运行工程,如果运行中又异常被抛出,即自动break进入调试。
此外,也可以通过前面提到的编辑菜单,进行修改所监听的异常类型、以及触发的时机(throw/catch)阶段。
符号断点(Symbolic Breakpoint)
符号断点(Symbolic Breakpoint),根据设定符号(可以是方法名、函数名、只要是通过目标-消息结构实现的一切符号)监听消息发送(方法、函数的执行)。
- 在XCode左侧导航里切换到断点导航,⌘+7;
- 单机“+”, Add Symbolic Breakpoint;
- 在出现的编辑Popover里的Symbol框里添加要加breakpoint的符号,例如:
viewDidLoad
:给所有viewDidLoad方法调用处添加breakpoint;[ViewController viewDidLoad]
:只给ViewController类的viewDidLoad方法调用处添加breakpoint。
断点追踪(Watch point)
断点追踪(Watch Breakpoints),顾名思义就是在断点处追踪某个变量值得变化,这是个很实用的功能。
例,在SampleClass里添加一个int型属性total;
- 在需要开始监听的位置加一个breakpoint,本例加载初始化从开始就监听total属性的变化;
- 运行,断点触发之后,在下发左侧的调试窗口中找到要监听变化的变量,本例为
_total
(因为total是用@propty构造器生成的,构造器在生成getter和setter的时候,同时还自动检测/生成一个带_前缀的成员变量); - 在_total上右键,watch “_total”;
- 点击“继续运行”按钮(原暂停按钮),继续运行,每当_total的数值发生变化的时候,都会暂停并且在右侧控制台输出变化前后的值。
断点动作(Breakpoint Action)
在前面编辑断点的时候,细心的读者应该已经发现在弹出的Popover了有个Action选项,可以添加6种Action: AppleScript
、 Capture GPU Frame
、 Debugger Command
、 Log Message
、 Shell Command
、 Sound
。Nori比较常用的是Log Message & Debugger Command。
Log Message
Log Message下分两种方式,一种是控制台输出,一种是给哥读出来。还以前边的SampleClass为例。
- 修改ViewController如下,并在38行添加断点:
- 编辑断点,为了验证在条件/约束框(Condition)里添加条件“
number > 1
”,即在参数number>1时才触发断点;添加Action,并选择Log Message, 输入“The current number is @number@, and the current total is @obj.total@.
”至于是输出到控制台(Log message to console)还是让它蹩脚的读出来(Speak message)自己决定吧。 - 运行,结果为“The current number is 2, and the current total is 1.”,如下。
注:…total is 1., 再次提醒大家,breakpoint触发的时,其所在的行(以及所有暂停状态游标所在的行),都尚未执行。本例中即obj.total += 2,如果将本例中的断点拖动到39行,那么结果就是“The current number is 2, and the current total is 3.”。
Debugger Command
另外说明,po
之后可以跟变量名,也可跟方法和函数的调用,例如,po obj
,po [self doSomeAction]
(doSomeAction是自己写的方法)。因此,在处理异常和其他断点的时候,我们可以通过po <方法调用>
来触发相应的处理方法,举个极端的例子:
- 添加一个方法-dealNumber:,目的是凡是number超过1(breakpoint条件决定),就给他砍去1刀(dealNumber决定)。
- 修改断点:
po number = [self dealNumber:number]
- 运行结果:
其中1是断点触发时po
后方法调用的返回值,2是NSLog(@"%d", obj.total);
。可见经过此折腾,原本应该是3的结果变成2了。
Sound
操作基本同上,为什么拿出来说呢?因为很实用,Nori以前做过不少的数据容错处理,为了不耽误时间把机器的声音开大,然后,run,再然后……健个身,和妹子聊回天啥的,哈哈。
其他的就不说了,提醒一点,一个breakpoint里Action可以加多种,多个。Actions组合使用可以大大提高调试的效率。
Quick Look
这应该比较常见,有的时候不小心就会出来,5.1以后大家应该都比较熟知。所以不多做介绍,附个简单教程。
调试仪表盘(Debug Gauges)& Instruments
这个太高大上了,我就不叨叨啥了。……呵呵,有点烂尾了,以后有空再叨叨吧……
以上是@Nori比较常用的调试手段,比较粗陋,学习更多Debug方法请走传送门:Debugging in Xcode、Apple Developer Support - Debugging。如有谬误欢迎斧正,如果还有什么你知道的我没提到的,欢迎分享。