我学到的调试技巧,主要有两个: 1、打印 2、调试器
打印
打印的主要麻烦是,你不能在一个地方完成所有打印输出,但如果把打印语句写到处是,又会给后面的删除或注释带来很大的工作量。我学会了用“#ifdef DEBUG“。就像它的字面意思一样,里面的语句或设置,只会在DEBUG 的时候才会执行。这使得我们可以非常安心在源码上插满打印语句,来监控我们的程序运行。 以iOS开发为例,在工程文件”XXX.pch”(通常在”Supporting Files”目录下)的”#ifdef OBJC”和与它配套的”#endif”之间插入:
//#ifdef DEBUG
////#define CLog(format, ...) NSLog(format, ##VA_ARGS); //#define CLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), PRETTY_FUNCTION, LINE, //##VA_ARGS);
//#else
//#define CLog(format, ...)
//#endif
然后,你就可以放心大胆地在源码插满“CLog“语句,而不用担心打印会影响到程序的性能。 如果你细心,会发现“CLog“只是一个长”NSLog”的代替。 我们来看看这个长“NSLog“和我们之前常用的短“NSLog“在打印结果上有什么不同。 语句: NSLog(@"viewDidLoad"); CLog(@"viewDidLoad");
打印输出: 2013-12-26 06:00:27.007 SOAP Test[2522:70b] viewDidLoad 2013-12-26 06:00:27.008 SOAP Test[2522:70b] -[ViewController viewDidLoad] [Line 33] viewDidLoad
注意多出来的“-[ViewController viewDidLoad] [Line 33]“,它指明了打印语句所在的类、方法,以及所处该类源码中具体行数。 哗,这实在是太方便了!
那插打印语句又有什么技巧呢? 首先,在所有代码块的头尾都插上“方法名 Begin“和"方法名 End"。这样做的好处是,只要你一运行程序,你就可以通过配对出现的"Begin"和"End"来监控一个代码块是否执行完毕,也可以此观察代码块的跳转关系,然后把代码块调整到更合理的位置。 第二,每个代码块结束前打印能代表该代码块的变量或对象。这样,你可以监控代码块是否实现正确的输出。 第三,每个代码块开始前打印需要控制的方法参数。这样,你可以监控调用代码块时是否给到正确的输入。 第四,插入有助于提高代码可读性的语句。这样,可以把你打印结果输出得更容易于别人理解,方便分享。
调试器
你可以在源码中的任何一个你想要停下来的地方插入断点,运行一下程序,就会停在断点所在的地方。 lldb的基本使用,网上有很多介绍。我想提的是"frame variable"。它把作用域内的所有变量都全打印出来,可以帮你发现一些你没注意到的变量值。