为Objective-C编程调试技巧(译)
http://www.cocoawithlove.com/2008/10/debugging-tips-for-objective-c.html
这篇文章是关于从程序获得额外的信息在运行时。Xcode和GDB都支持广泛的信息获取工具 - 但你需要知道,他们在那里。下面是一些Objective-C的具体gdb的提示和指令,所有的Cocoa程序员应该知道的。
说话直接用gdb
调试器控制台窗口很聊得来的gdb的方式。从运行菜单(或type命令移-R)显示在Xcode的控制台窗口。
您只能发送命令到GDB当程序暂停(停止在断点处)。当你有(GDB)提示,然后你可以跟gdb调试程序。
大多数命令,gdb的接受,Xcode的通过显示在调试器窗口中的值会自动为你处理。所以我打算忽略其中的大多数。
"po":打印对象
打印对象命令显示一个Objective-C的对象的文本表示。
想象一下,你想知道为什么你的电话:
- ( ID )getFirstObjectFrom:( NSDictionary中
*)stringDictionary
{ 返回
[stringDictionary
objectForKey : @“FIRSTKEY” ];
} |
将返回零。设置就行了断点,当调试器停止在这一点上,进入调试器控制台,然后键入:
po stringDictionary |
命中回报和gdb会给出结果。就我而言,它是:
{ FIRSTKEY = firstObject;
secondKey = secondObject;
thirdKey = thirdObject;
} |
我应该使用的键名是@“FIRSTKEY”以小写‘F‘。问题解决了。
在这种情况下,gdb的被调用描述的方法的NSDictionary生成的字符串。该描述方法用于所有的整个可可来生成对象的字符串,你可以重写它,以提供您的对象的字符串表示形式。
Xcode的数据格式化
如果调试器仍然停在同一条线上,你在Xcode打开调试器窗口,变量的“参数”列表将包含一个条目stringDictionary。对于一个的NSDictionary这样的对象,Xcode中会显示在“摘要”栏“3键/值对。”
此信息来自一个“数据格式化”,也就是设立的NSDictionary默认。你可以了解他们在苹果的Xcode调试指南:使用数据格式化。从本质上讲,数据格式化告诉Xcode中如何获取一些数据在列中显示。
不同的数据格式可以看出,如果我们在右键单击stringDictionary行中的调试器窗口,然后从上下文菜单中选择“打印说明以控制台”。
打印
的说明
stringDictionary :
< CFDictionary
0 x 3 5 EDD 0
[ 0 XA 0 b 0 6 1 7 4 ]> {类型=不变,计数= 3 ,容量= 3 ,对=(
0
:< CFString字符串
0 x 2 0 9 0
[ 0 XA 0 b 0 6 1 7 4 ]> {内容=“secondKey”} = < CFString字符串
0 x 2 0 8 0
[ 0 XA 0 b 0 6 1 7 4 ]> {内容=“secondObject”}
1
:< CFString字符串
0 x 2 0 b 0
[ 0 XA 0 b 0 6 1 7 4 ]> {内容=“thirdKey”} = < CFString字符串
0 x 2 0 一 0
[ 0 XA 0 b 0 6 1 7 4 ]> {内容=“thirdObject”}
3
:< CFString字符串
0 x 2 0 7 0
[ 0 XA 0 b 0 6 1 7 4 ]> {内容=“FIRSTKEY”} = < CFString字符串
0 x 2 0 6 0
[ 0 XA 0 b 0 6 1 7 4 ]> {内容=“firstObject”}
)} |
如果没有数据格式,“打印说明以控制台”将输出相同的信息为“宝”命令。在这种情况下,虽然,它清楚地输出一个更详细的描述的字典,具有完整的类型信息和索引。正是这个CFDictionary数据格式化驻留是未知的我(它不是在为Xcode中的数据格式化的标准位置)。
其他的“打印”命令
该PO在gdb的命令永远只能显示结果从调用描述一个物体上,但更普遍的打印命令将让我们做其他事情。
我可以使用下面的命令:
打印( 字符 *)[[stringDictionary
描述 ]
的CString ]
|
这将有输出:
$ 1
= 0 x 3 6 0 0 3 1
“{\?FIRSTKEY = firstObject; \?secondKey = secondObject; \ n
thirdKey = thirdObject; \ n}的“
|
这是相同的数据表示为宝为例,减去漂亮的格式。
我还可以使用下面的命令:
打印( 整型 )[stringDictionary
retainCount ]
|
制定出的保留计数stringDictionary帮我工作了,为什么内存或者是没有得到释放。
由于与PO命令和“打印说明以控制台”在Xcode相等,则打印命令有一个Xcode的等价了。您可以从运行- >显示- >表达式菜单打开表达式窗口。
“信息符号”:得到一个地址的符号名
最后的gdb命令我要在这里讨论的是信息符号的地址,返回与给定的存储单元关联的任何变量或代码的名称地址。
想象一下,例如,你的内存地址“混淆0xa0b06174通过“打印说明到控制台”输出显示“stringDictionary那我上面显示。所有您需要做的则是类型:
信息符号 0 XA 0 b 0 6 1 7 4
|
进入调试器控制台和gdb会告诉你:
__kCFAllocatorSystemDefault 在
部分LC_SEGMENT 。__DATA 。__data
的/系统/库/框架/的CoreFoundation 。框架 /版本/ A /的CoreFoundation
|
这是系统默认的CoreFoundation内存分配器。也许这还并不意味着一大堆,但至少我们知道在该地址中声明的对象的名称。
当你得到一个异常日志,看起来像这样更加有用的是:
2 0 0 8 - 1 0 - 2 6
13 : 2 5 : 4 3 .381
CrashExample [ 4 1 7 2 0 : 2 0 B] *** - [ TransitionView
doesntExist ]:无法识别的选择发送到实例 0 XF 4 FBB 0
2 0 0 8 - 1 0 - 2 6
13 : 2 5 : 4 3 0.383
CrashExample [ 4 1 7 2 0 : 2 0 B] *** 终止
应用程序因未捕获的异常‘NSInvalidArgumentException‘,
原因 :‘*** - [ TransitionView
doesntExist ]:无法识别的选择发送到实例 0 XF 4 FBB 0 ‘
2 0 0 8 - 1 0 - 2 6
13 : 2 5 : 4 3 .385
CrashExample [ 4 1 7 2 0 : 2 0 B]
堆栈 :(
2 5 2 8 0 1 3 8 0 4 ,
2 4 7 8 5 0 3 1 4 8 ,
2 5 2 8 0 4 2 9 2 0 ,
2 5 2 8 0 3 6 2 7 2 ,
2 5 2 8 0 3 6 9 2 0 ,
1 1 0 7 6 ,
1 1 8 8 0 ,
8 1 6 1 7 4 8 8 0 ,
8 1 6 1 7 4 8 8 0 ,
8 1 6 5 0 4 0 3 6 ,
8 1 6 5 0 0 9 6 0 ,
8 1 6 2 5 8 7 9 2 ,
8 1 6 2 0 2 1 2 8 ,
8 1 6 1 9 9 5 0 8 ,
8 2 9 0 0 5 5 2 0 ,
8 2 9 0 1 4 7 7 2 ,
2 5 2 7 5 6 4 4 5 6 ,
8 2 9 0 0 7 7 2 4 ,
8 1 6 1 7 3 0 1 6 ,
8 1 6 2 1 2 3 3 6 ,
9 8 8 8 ,
9 6 6 8
) |
日志告诉我们发生了,因为一个无法识别的选择发送到一个对象,但我们可能不知道在这个程序中发生的问题。
你可以看一下堆栈跟踪,看到的最高地址是“小”(即可能是在你的代码,而不是默认的库)为“11076”,然后给gdb的命令:
信息符号 1 1 0 7 6
|
和gdb会告诉你:
- [ CrashExampleAppDelegate
performTransition ] + 8 8
在
部分LC_SEGMENT 。__TEXT 。__text
的/用户/哑光/项目/ CrashExample 。应用程序 / CrashExample
|
它告诉我们,这个问题发生在方法performTransition。
更新:由于在评论中指出的“G”,还有一个更好的方法来确定的方法和代码行的在这种情况下一个地址。信息行*
11076
会以原始的源代码文件中的行,而不仅仅是字节从函数的起始偏移量。
这使得它烦人,在Mac OS X 10.5 -如果你看看5上面的地址列表中的11076,他们都与抛出异常本身相关联objc_exception_throw只返回前5名地址,因为这5个地址往往是相同的异常抛出地址(他们不说任何事情,为什么抛出异常)。
地址和符号调试器外
既然我提到坠毁阅读文件和查看内存地址:GDB不这样做,如果最好的方法别人给你的内存地址。要做到这一点,你应该从他们所使用的确切构建有。的dSYM文件。
如果你不知道如何生成。的dSYM文件,转到项目 - >编辑项目设置 - >生成 - >生成选项 - >调试信息格式,并确保你有一个的dSYM文件。你应该保留这些文件大约为每个建立你释放。iPhone SDK的默认生成这些文件,但您将需要打开的手动上为Mac版本。
调用的命令行如下:
dwarfdump-A的<em> NameOfdSYMFile </ em>的 |
这会告诉你该文件中的每一知道地址。所有您需要做的是找到最接近上述任一地址到您崩溃的位置,这将是罪魁祸首方法或函数。
然而,这是一个非常笨拙的方法。为了得到一次单一的地址,将。的dSYM和。的应用程序,它是指在同一目录下,你可以使用ATOS命令可以获得一个地址一个符号。上面显示的CrashExample崩溃的bug,你可以调用这个命令是这样的:
ATOS-O CrashExample 。应用程序 /内容/的MacOS / CrashExample
拱PPC 1 1 0 7 6
|
这将使结果:
- [ CrashExampleAppDelegate
performTransition ]( 在
CrashExample)(CrashExampleAppDelegate 。米 : 9 4 )
|
最后,如果你想从一个崩溃日志得到所有的地址,你可以使用苹果的symbolizecrashlog脚本。这一发现的。应用程序和。的dSYM文件为。的Crash.log文件,并调用ATOS得到遏制。内的每一个可能的符号感谢millenomi指出这一点的意见。
结论
有很多详细信息,在调试的时候挤出不仅仅是在你的变量的原始值。有机会获得这些信息,在调试过程中,可以跟踪Bug,只是有点快。