iOS 调试(一)

       LLDB:lower level debugge/底层调试器。

本节要介绍的所有的内容几乎都是针对LLDB的,因为苹果已将GDB替换成LLDB。Xcode4.0开始 Xcode4.2,他们默认的编译器都是LLVM3.0,使用Clang作为编译器前端,取代了GCC作为编译器前端会有很多优势;到了Xcode4.5(同iOS6同时发布)默认的编译器就是LLVM4.0。LLVM搭配Clang,可以提供更快更好地编译过程,更好地支持代码补全。

1.  LLDB

                      LLDB是用LLVM中可以重用组件构建的额下一代高性能调试器,包括完整的LLVM编译器,其中就有LLVM的Clang表达式解析器和反汇编程序。对于开发者而言,这意味着LLDB能理解你的编译器所能理解的语法,包括OC字面量,OC属性的点标记法。

                      前一版调试器GDB性能并不好,像po self.view.frame 他是不能理解的,你需要输入po [[self view] frame]。当替换编译器时,APPLE就需要改进调试器,由于GDB是一个整体,所以没办法解决。开发者需要重新编写一个调试器。LLDB是模块化的,而为调试器提供API支持和脚本编程接口是设计目标之一。 LLDB命令行调试器会通过这个API链接到LLDB库。

 

实用LLDB命令

命令名          用法          说明


1.1     dSYM文件

 Xcode的调试信息文件称为 dSYM文件(因为扩展名叫为.dSYM),又叫调试信息文件,它存储着与目标相关的调试信息。

他会在每次构建工程时自动创建:


用任何一种编程语言编写的代码都需要一个编译器,将这些代码翻译成可以被IDE理解的某种中间语言,或者是可在机器的体系结构上直接运行的原生机器码。调试器通常会集成在开发环境中。Xcode支持放置断点使应用停止运行,从而查看代码中变量的值。也就是说,调试器能够实时的使应用停止运行,这样就可以查看变量和寄存器。

有两类重要的调试器:

                     a.符号调试器 :能够在调试代码时显示应用中使用的符号或变量。跟机器语言不同,符号调试器容许你观察代码中得符号,而不是寄存器和地址

                     b.机器语言调试器:能够在运行到断电时显示你想过来的汇编代码,容许你观察寄存器的值和内存地址。

让符号调试器工作起来,需要一个编译过的代码和你编写的源代码之前的链接或映射。这正是调试信息文件中所包含的内容。

调试器使用这个调试信息文件将编译过的代码---不管是中间代码还是机器码----映射回源代码。可以将调试信息文件当做游客浏览陌生城市时参考的地图。调试器能参考调试信息文件,根据你再源代码中放置的断电让应用停在正确的位置。

 在XCODE编译项目之后,会在app旁看见一个同名的dSYM文件。他是一个编译的中转文件,简单说就是debug的symbols包含在这个文件中。

他有什么作用? 当release的版本 crash的时候,会有一个日志文件,包含出错的内存地址, 使用symbolicatecrash工具能够把日志和dSYM文件转换成可以阅读的log信息,也就是将内存地址,转换成程序里的函数或变量和所属于的文件

 

1.1     符号化

   包括LLVM在内的编译器都是用来将源代码转换成汇编代码的。所有汇编代码都有一个基地址,。你定义的变量,用到的栈和堆都会依赖这个基地址。每次运行应用时,这个基地址都会改变,尤其是在iOS4.3及以上版本的操作系统中,这些操作系统都采用了地址空间布局随机化的机制。符号化使用方法名和变量名来替换基地址的过程。基地址是应用的入口地址,通常就是main方法,除非你是在写一个静态库。可以符号化其他符号,方法是计算他们相对基地址的偏移,然后将他们映射到dSYM文件中。符号化过程再用Xcode调试应用时才会进行,或者在用Instruments做性能计数分析时进行。

                 

 

2.   断点

    添加到工程中的断点会自动在断点导航面板中列出。可以使用快捷键组合Cmd+6来访问断点导航面板。断点导航面板还支持为异常和符号设置断点。

2.1. 异常断点

在代码有问题导致抛出异常时,异常断点会停止程序的执行。Foundation.framework的NSArray、NSDictionary或UIKit类(比如UITableView方法)中的一些方法会在不能满足特定条件的情况下抛出异常。这些场景包括尝试改变NSArray或是尝试访问越界的数组元素。UITableView会在将行数声明为“n”而没有给每行都提供一个单元格时抛出异常。调试异常在理论上比较容易,但理解造成异常的源相当复杂。应用在崩溃时可能只会在日志中显示造成崩溃的那条异常。这些Foundation.framework方法会在整个工程中都用到,不设置异常断点,即使看了日志也不知道究竟发生什么了。设置了异常断点后,调试器会在异常抛出的瞬间暂停程序的执行,但在捕获异常之前,你需要在断点导航面板中查看崩溃了的那个线程的栈轨迹。

为了方便理解,我们比较一下使用和不使用异常断点调试应用的不同。

在Xcode中创建一个空应用(任何模板都能工作)。在应用委托中,添加以下行:

NSLog(@"%@", [@[] objectAtIndex:100]);

它会创建一个空数组,然后访问第一百个元素,并记录它。由于这种用法并不符合规范,执行该程序时它会崩溃,控制台会有如下输出,Xcode会跳转到main.m:

2012-08-27 15:25:23.040 Test[31224:c07] (null)

libc++abi.dylib: terminate called throwing an exception

(lldb)

 现在:

2013-07-24 09:39:08.776 testDemo[961:c07] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 100 beyond bounds for empty array'

*** First throw call stack:

(0x1c90012 0x10cde7e 0x1c45b44 0x1ec3 0xf157 0xf747 0x1094b 0x21cb5 0x22beb 0x14698 0x1bebdf9 0x1bebad0 0x1c05bf5 0x1c05962 0x1c36bb6 0x1c35f44 0x1c35e1b 0x1017a 0x11ffc 0x1bd2 0x1b05)

libc++abi.dylib: terminate called throwing an exception

但看看这难懂的日志消息,没人晓得背后发生了什么。要调试这样的异常,需要设置一个异常断点。

可以在断点导航面板中设置一个异常断点。打开断点导航面板,点击左下角的+按钮,选择Add Exception Breakpoint,接受默认设置,新加一个断点,如图所示。



Exception:可选 all 所有语言引起的异常,objective-c语言和c++语言引起的异常。

Break:可选onThrow和onCatch。

Action:可在程序断点执行后增加额外动作(Applescript,捕捉动画帧速,调试器命令(lldb),输入log记录,终端命令(shell),播放声音)

例如:Debugger Commond中可填入

po item 输出 item变量的值 

bt 表示输出 方法调用堆栈信息

 

下图 增加一个异常断点

再次运行该工程。你应该能看到调试器暂停了应用的执行,程序正好停在抛出异常的那行,如图所示。


Xcode在设置断点的位置停止执行应用

异常断点能帮你理解异常的起因。我在新建工程时,要做的第一件事就是设置一个异常断点。我强烈推荐这么做。

 

如果想快速运行应用而不想在任何断点处停留,那么可以在键盘上用快捷键Cmd+Y来禁用所有断点。

 

2. 符号断点

符号断点会在执行到特定符号时暂停程序。符号可以是一个方法名、类中的一个方法或者任何C方法(objc_msgSend)。

可以在断点导航面板中设置符号断点,跟设置异常断点差不多,不过要选择符号断点而不是异常断点。现在,对话框中输入了你关注的符号,如图



图 增加一个符号断点

 

Symbol:填入你想检测消息发送实体的方法

(例如:-[NSException raise],-号是实例方法,+号是类方法)。

你也可以输入:

objc_exception_throw

malloc_error_break  //跟踪调试释放了2次的对象

 -[NSObjectdoesNotRecognizeSelector:]  //向某个object发送没有的方法

Module:填入是否在一个dylib中,默认不用填。

Conditon:填入条件,例如:

(BOOL)[itemisEqualToString:@"test"]

前面的(BOOL)是必须的。否则console会提示类型不符号,导致条件不能生效。

意思是item(NSString)是test时停下。

同样可以写一下判断的方法比如用来确定类类型的isKindOfClass:,确定对象在继承体系中的位置的isMemberOfClass:,判断一个对象是否能接收某个特定消息的respondsToSelector:,判断一个对象是否遵循某个协议的conformsToProtocol:,以及提供方法实现地址的methodForSelector:。

Ignore:忽略几次。

Action:同上表示在执行后附加动作。

现在键入application:didFinishLaunchingWithOptions:,然后按下回车键。构建并运行应用。你应该看到调试器会在程序刚开始运行时就停止执行应用,并显示栈轨迹。

你查看的符号除了在application:didFinishLaunchingWithOptions:中放置了一个断点,再没有其他好处。符号断点通常用来观察你要关注的方法,比如:

-[NSException raise]

malloc_error_break

-[NSObject doesNotRecognizeSelector:]

事实上,前一节创建的第一个异常断点与指向[NSException raise]的符号断点的意思是一样的。

malloc_error_break和[NSObject doesNotRecognizeSelector:]对调试与内存相关的崩溃非常有帮助。如果应用崩溃了并抛出EXC_BAD_ACCESS,那么在其中一个或全部两个符号上设置断点能够帮助你定位问题。

3. 编辑断点

创建的每个断点都可以在断点导航面板中修改。按住Ctrl键并点击断点,然后从菜单中选择Edit

Breakpoint的方式来编辑断点。你会看到一个断点编辑页,如图


图19-5 编辑断点

通常,断点会在每次执行到该行时停止程序的执行。你可以编辑断点来设置一个条件,从而创建一个条件断点,只在满足设定的条件时该断点才会执行。为什么这种断点会有用呢?假设你在遍历一个大型数组(*n*>10000),很确定5500之后的对象都有问题,你想知道为什么会出问题。常见的做法是,(在应用的代码中)编写额外的代码检查5500之后的索引值,然后在调试环节结束后删除这段代码。

举个例子,你可能会写出如下代码:

for(int i = 0 ; i < 10000; i ++) {

    if(i>5500) {

      NSLog(@"%@", [self.dataArray objectAtIndex:i]);

    }   

  }

并在NSLog处设置一个断点。更简洁的做法是向断点增加这个条件。在图19-5中,文本框是用来添加条件的。将这个条件设为i>5500,然后运行应用。现在,断点只会在满足这个条件时停止应用的执行,而不是每次循环都停下来。

你可以定制断点来打印一个值、播放音频文件,或是执行一段动作脚本(添加了动作脚本的话)。举个例子,如果你正在遍历的对象是一些用户,想知道某个用户是否在这个列表中,这时可以编辑断点使其在运行到你关注的对象时再停下来。除此之外,在这个动作中,还可以选择一些音频片段来播放,执行一段AppleScript或其他功能。点击Action按钮(参考图19-5),选择自定义动作Sound。现在,在断点处Xcode会播放你选择的音乐片段,而不是停下来。如果你是一名游戏开发人员,你感兴趣的可能是在特定条件发生时捕捉一个OpenGL ES帧,这个选项在Action按钮中也可以找到。

 4. 共享断点

断点现在与要保存到版本控制系统中的代码(或者只是代码片段)关联了起来。Xcode 4(及以上版本)允许将断点提交到版本控制系统,从而与合作者共享它们。你所要做的就是按住Ctrl键并点击一个断点,然后点击Share。你的断点现在已经保存到了工程文件包的xcshareddata目录中。将该目录提交到版本控制系统中,就可以跟团队中的所有其他程序员共享你的断点了。

4.   观察点(没用过)

利用断点,能够在执行到特定行时暂停程序的执行。利用观察点,可以在某个变量中保存的值发生变化时暂停程序的执行。段差点可以帮助解决与全局变量有关的问题,追踪具体是哪个方法改变了特定的全局变量。观察点和断点很像,当不是在执行到某段代码时停止执行,而是在数据被修改时停止执行。

           观察点可能不常用,不过,用它来跟踪单例,或者其他全局变量时会很有用。

 默认情况下,观察窗口会列出局部作用域内的变量,在观察窗口中按下Ctrl键并点击一个变量。再点击Watch<var> 菜单,就在哪个变量上添加了一个观察点。观察点会在断点导航面板中列出。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值