Xcode常用调试技巧
Enable NSZombie Objects(开启僵尸对象)
Enable NSZombie Objects可能是整个Xcode开发环境中最有用的调试技巧。这个技巧非常非常容易追踪到重复释放的问题。该技巧会以非常简洁的方式打印指出重复释放的类和该类的内存地址。
怎么开启僵尸对象呢?首先打开“Edit Scheme”(或者通过热键?<),然后选择Diagnostics选项卡,勾选Enable NSZombie Objects选项。
现在我们可以关掉ARC来测试重复释放的问题、异常和更容易产生的Crashs,但是即使开启ARC,重复释放和与内存相关的Crashs也经常发生。现在假设我们犯了一些错误导致了重复释放的发生来观察将要发生什么。
1 UIView* view = [[[UIView alloc] init] autorelease]; 2 //... 3 //do something with view...clearly forgetting that it has been autoreleased. 4 // 5 [view release];
现在运行这段代码会发生重复释放的问题,程序将崩溃在主函数,会像下面这样:
Enable NSZombie Objects会让调试器看起来像这样:
这个例子看起来作用非常小,但是对于一个大工程通过Enable NSZombie Objects打印的信息是非常有用的。
对于所有异常添加Global BreakPoint(全局断点)
当 你的应用异常或者崩溃的时候Xcode喜欢做的一件事情是直接跳到主函数,就像第一个调试技巧中用到的那个例子一样。如果异常或者崩溃能够停 留在异常或者崩溃发生的地方那该是多好呀。幸运的是有一个方法可以做到。Xcode有一个漂亮的功能叫做异常断点,允许我们在异常发生的时候在异常发生的 地方放一个断点。你可以指定具体的异常也可以指定所有的异常。
怎样开启异常断点呢?工程切换到异常浏览窗口,点击下方左侧的“Add Breakpoint”按钮,然后选择“Add Exception Breakpoint”确保可以捕获所有异常。
现在异常发生时调试器不会直接跳转到主函数了,而是停留在了异常发生的地方。
这个断点给了我们一个调试异常非常好的开始的点,不用我们再去遍历代买去找异常发生的地方了。
Static Analyzer(静态分析)
Static Analyzer是一个非常好的工具去发现编译器警告不会提示的问题和一些个人的内错泄露和死存储(不会用到的赋了值的变量)错误。这个方法可能大大的提高内存使用和性能,以及提升应用的整体稳定性和代码质量。
怎么打开Static Analyzer?打开Xcide的“Product”菜单,选择Analyzer选项(或者使用热键? shift B)。正像你看到的下面的截图,Static Analyzer会捕获任何应用内的可能的问题然后以蓝色的警告展示出来。
我们也可以设置我们的应用在我们编译应用的时候自动开启Static Analyzer,打开项目project文件,设置“Run Static Analyzer”为YES,如下图:
Condational Breakpoints(条件断点)
最后一个调试技巧条件断点,这只是普通的断点,当变量满足一定条件时程序停止。这个调试技巧在当你想要捕获一个循环中的变量的特定值或者一些不常发生的情况时是非常有用的,而不用你每次迭代都停止来查看。
怎样开启条件变量?只不过是添加一个普通断点,然后右键点击断点选择“Edit Breakpoint”,这时就打开了一个断点编辑器,你可以在这里设置断点条件(以及一些其他的断点设置),然后选择“Done”,这个调试技巧非常简单吧。
重写object的respondsToSelector方法
在iphone开发的时候EXC_BAD_ACCESS这个bug时不容易找到原因的。
首先说一下EXC_BAD_ACCESS 这个错误,可以这么说,90%的错误来源在于对一个已经释放的对象进行release操作,或者操作一个在循环代码中被修改的序列中的对象。虽然使用NSZombieEnabled变量可以帮助你找到问题所在,但有的时候,即使通过设置NSZombieEnabled变量,还是不能定位到问题所在,这个时候,你可以试试重写object的respondsToSelector方法,显示出现EXEC_BAD_ACCESS前访问的最后一个object,下面是具体的步骤:
a、在每个类并且在 other c flags中加入-D _FOR_DEBUG_(记住请只在Debug Configuration下加入此标记)。这样当你程序崩溃时,Xcode的console上就会准确地记录了最后运行的object的方法。的实现文件(.m,.mm)文件中,添加如下代码:
-(BOOL) respondsToSelector : (SEL)aSelector { printf("SELECTOR: %s\n", [NSStringFromSelector(aSelector) UTF8String]); return [super respondsToSelector:aSelector]; }
b、并且在 other c flags中加入-D _FOR_DEBUG_(记住请只在Debug Configuration下加入此标记)。这样当你程序崩溃时,Xcode的console上就会准确地记录了最后运行的object的方法。
让XCode反馈足够多的信息
在Edit–>Scheme里面 找到Arguments ,在Environment Variables这里添加
把下面2个值设置成YES
NSAutoreleaseFreedObjectCheckEnabled
NSDebugEnabled
这种方法非常好用,建议在建立一个工程的时候,加入此设置
unrecognized selector send to instancd 快速定位
在Debug菜单中Breakpoints->Create Symbolic Breakpoint
在Symbolic中填写如下方法签名
- -[NSObject(NSObject) doesNotRecognizeSelector:]
设置完成后再遇到类似的错误就会定位到具体的代码。
你也可以这样做,在系统抛出异常处设置断点:和上面的含义一样
程序不知道跑到哪个地方就 crash 了,而 crash 又很难重现。
保守的做法是: 在系统抛出异常之前设置断点. (具体来说是在 objc_exception_throw 处设置断点。)
设置步骤为:
1. 在 XCode 按 CMD + 6,进入断点管理窗口;
2. 然后点击右下方的 +,增加新的 Symbolic Breakpoint。
3. 在 Symbol 一栏输入:objc_exception_throw,然后点击 done,完成。
高级装逼技术-GDB
XCode 内置GDB,我们可以在命令行中使用 GDB 命令来调试我们的程序。下面将介绍一些常用的命令以及调试技巧。
po 命令:为 print object 的缩写,显示对象的文本描述(显示从对象的 description 消息获得的字符串信息)。
比如:
上图中,我使用 po 命令显示一个 NSDictionary 的内容。注意在左侧我们可以看到 dict 的一些信息:3 key/value
pairs,显示该 dict 包含的数据量,而展开的信息显示 isa 层次体系(即class 和
metaclass结构关系)。我们可以右击左侧的 dict,选中“Print Description of "dict"”,则可以在控制台输出
dict 的详细信息:
Printing description of dict: {type = immutable dict, count = 3, entries => 0 : {contents = "first"} = {contents = "one"} 1 : {contents = "second"} = {contents = "two"} 2 : {contents = "third"} = {contents = "three"} }
print 命令:有点类似于格式化输出,可以输出对象的不同信息:
如:
(gdb) print (char *)[[dict description] cStringUsingEncoding:4]
- $1 = 0x1001159c0 "{\n first = one;\n second = two;\n third = three;\n}"
(gdb) print (int)[dict retainCount]
- $2 = 1
(gdb)
注:4是 NSUTF8StringEncoding 的值。
info 命令:我们可以查看内存地址所在信息
比如 "info symbol 内存地址" 可以获取内存地址所在的 symbol 相关信息:
(gdb) info symbol 0x00000001000017f7
- main
+ 343 in section LC_SEGMENT.__TEXT.__text of
/Users/LuoZhaohui/Library/Developer/Xcode/DerivedData/RunTimeSystem-anzdlhiwvlbizpfureuvenvmatnp/Build/Products/Debug/RunTimeSystem
比如 "info line *内存地址" 可以获取内存地址所在的代码行相关信息:
(gdb) info line *0x00000001000017f7
- Line
62 of
"/Users/LuoZhaohui/Documents/Study/RunTimeSystem/RunTimeSystem/main.m"
starts at address 0x1000017f7 and ends at 0x10000180a .
show 命令:显示 GDB 相关的信息。如:show version 显示GDB版本信息
(gdb) show version
- GNU gdb 6.3.50-20050815 (Apple version gdb-1708) (Mon Aug 8 20:32:45 UTC 2011)
- Copyright 2004 Free Software Foundation, Inc.
- GDB is free software, covered by the GNU General Public License, and you are
- welcome to change it and/or distribute copies of it under certain conditions.
- Type "show copying" to see the conditions.
- There is absolutely no warranty for GDB. Type "show warranty" for details.
- This GDB was configured as "x86_64-apple-darwin".
help 命令:如果忘记某条命令的语法了,可以使用 help 命令名 来获取帮助信息。如:help info 显示 info 命令的用法。
(gdb) help info
- Generic command for showing things about the program being debugged.
- List of info subcommands:
- info address -- Describe where symbol SYM is stored
- info all-registers -- List of all registers and their contents
- info args -- Argument variables of current stack frame
- info auxv -- Display the inferior‘s auxiliary vector
- info breakpoints -- Status of user-settable breakpoints
- info catch -- Exceptions that can be caught in the current stack frame
- info checkpoints -- Help
- info classes -- All Objective-C classes
- ......
- Type "help info" followed by info subcommand name for full documentation.
- Command name abbreviations are allowed if unambiguous.
常见GDB命令
-
-
-
- break NUM 在指定的行上设置断点。
- bt 显示所有的调用栈帧。该命令可用来显示函数的调用顺序。
- clear 删除设置在特定源文件、特定行上的断点。其用法为:clear FILENAME:NUM。
- continue 继续执行正在调试的程序。该命令用在程序由于处理信号或断点而导致停止运行时。
- display EXPR 每次程序停止后显示表达式的值。表达式由程序定义的变量组成。
- file FILE 装载指定的可执行文件进行调试。
- help NAME 显示指定命令的帮助信息。
- info break 显示当前断点清单,包括到达断点处的次数等。
- info files 显示被调试文件的详细信息。
- info func 显示所有的函数名称。
- info local 显示当函数中的局部变量信息。
- info prog 显示被调试程序的执行状态。
- info var 显示所有的全局和静态变量名称。
- kill 终止正被调试的程序。
- list 显示源代码段。
- make 在不退出 gdb 的情况下运行 make 工具。
- next 在不单步执行进入其他函数的情况下,向前执行一行源代码。
- print EXPR 显示表达式 EXPR 的值。
- print-object 打印一个对象
- print (int) name 打印一个类型
- print-object [artist description] 调用一个函数
- set artist = @”test” 设置变量值
-
-
-
-
-
- expr gestureDisabled = 1 设置变量值
- whatis 查看变理的数据类型
-
-