前言在开发的时候调试和跟踪是肯定需要的,但是很多开发者虽然做了很多的项目,但是可能不知道在开发中有哪些调试命令可以帮助我们开发者越来越快好位置的问题。本文主要讲解在开发中如何使用LLDB进行调试。首先讲一些基础知识,主要是帮助新手学习调试。对于一些更高级的操作,倒是无所谓,但是如果你能掌握的话,发现问题会更方便、更快捷。LLDB的初步认识LLDB是XCode中为我们开发者内置的一个调试工具。至于不知道调试是什么,百度一下这个概念吧,不知道怎么形容。看看下图,你应该能大致明白什么是调试!我们加了一个断点,然后在断点处停了下来。我们在这里看到lldb了吗?我们可以通过lldb提供的命令进行操作。基本调试操作从上图来看,我们有八个按钮,先说前五个按钮:点击第一个按钮关闭该栏目,不可见。第二个按钮:如果为蓝色,则断点有效。如果您单击它并且它变成灰色,则所有断点都被禁用。第三个按钮:表示继续,它会让程序从断点处继续运行。我们点击这个按钮后,应用程序就会恢复正常运行。第四个按钮是:单步执行的意思是,每点击一次这个按钮,程序就会从我们的断点开始的地方往下执行一步。第五个按钮是:输入执行的意思。简单的说,如果我们当前的断点是在一个函数调用上,断点就会继续进入这个函数内部进行调试。第六个按钮是:jumpout意思是如果我们当前在一个函数中,就会跳出当前函数,回到函数的调用处。常用的p,po,call命令先看下图:下面是输入help命令时打印出来的,可以看出四者的区别:p--('expression--')Evaluateanexpression(ObjC++或Swift)在当前程序上下文中,使用用户定义的变量和当前范围内的变量。po--('expression-O--')在当前程序上下文中使用用户定义的变量和当前范围内的变量计算表达式(ObjC++或Swift)。print--('expression--')在当前程序上下文中计算表达式(ObjC++或Swift),使用用户定义的变量和当前范围内的变量。call--('expression--')在当前程序上下文中使用用户定义的变量和当前在范围内的变量计算表达式(ObjC++或Swift)。从官方描述来看,p、print、call是一样的,但是po不一样,输入一样但是输出不一样。po的输出是具体对象的内容。如果要以特定格式打印,则如下:(lldb)p/sblogName(__NSCFConstantString*)$9=@"表哥的技术博客"(lldb)p/xblogName(__NSCFConstantString*)$10=0x000000010921c0a8@"表哥的技术博客“(LLDB)P/TBLOGNAME(__NSCFCONSTANTSTRING*)$11=0B00000000000000000000000000000000000000000000100100100100100000000111101000@请参考PrintoutFormattinglldbDeclaringVariables我们可以使用e命令来定义变量,然后在调试中使用。看下面的例子:(lldb)eNSString*$str=@"http://www.henishuo.com"(lldb)po$strhttp://www.henishuo.com(lldb)eint$count=10(lldb)p$count(int)$count=10(lldb)eNSArray*itemArray=@[@"Test",@"Demo",@"huangyibiao"](lldb)po$count10我们用e声明str变量,那么下面的就可以用了。我们看到p命令打印出来的变量都是开头的变量了吗?我们在声明和使用的时候也需要加上$符号,就像PHP一样!在调试的时候,有时候想临时计算某个值进行比较,就可以这样做。您不需要在源代码中添加语句,然后添加语句进行打印。是不是更方便?调用变量API当我们在断点的时候定义了blogName变量,所以我们可以通过调试命令调用(lldb)po[blogNameuppercaseString]表哥的技术博客(lldb)po[blogNamesubstringFromIndex:2]切换到返回值类型当我们在不指定返回值类型的情况下调用API时,有时打印出来的东西我们是看不懂的。比如下面得到的结果应该是“mark”,但是不同类型打印的结果不一样:(lldb)po[blogNamecharacterAtIndex:0]26631(lldb)po(unsignedint)[blogNamecharacterAtIndex:0]26631(lldb)po(char)[blogNamecharacterAtIndex:0]'a'(lldb)po(NSString*)[blogNamecharacterAtIndex:0]]0x0000000000006807(lldb)po(unichar)[blogNamecharacterAtIndex:0]U+6807u'mark'AddbreakpointsIfwedon't一开始就加断点,但是调试开始后,我们想在其他地方加断点,那么我们可以很方便的通过命令加断点:(lldb)b?33Breakpoint9:where=OCLLDBDebugDemo`-[ViewControlleronButtonClicked:]+53atViewController.m:33,address=0x000000010921a6d5这是在当前class文件下的第33行添加断点,添加成功后会有提示,比如这里的提示就是在第33行添加断点成功.当然加断点的方式有几种,比如:(lldb)b?-[ViewControlleronButtonClicked:]Breakpoint4:where=OCLLDBDebugDemo`-[ViewControlleronButtonClicked:]+53atViewController.m:33,address=0x000000010921a6d5其实是在第33行添加断点。但是,如果我们想动态添加断点,我们可以使用b命令添加行号,这是最简单的。设置断点触发条件见下图,作者是如何设置触发条件的:我们在NSLog这一行设置条件,只有满足条件才会进入断点,不允许进入断点这里。并且当条件满足时,它会发出声音并打印提示。这种应用场景主要是在循环遍历数据的时候。如果要断点跟踪,只能用这个方法,除非你加了NSLog打印,但是这种代码需要手动加。只有在调试的时候才会想到加一些打印语句。这时候又得跑一遍,太慢了。如果你知道如何设置断点条件,那么就可以满足我们的需求,你可以直接设置条件。常用的打印视图层级当我们想知道一个视图的结构时,调用recursiveDescription方法打印出来,那么它的结构一目了然:(lldb)po[self.viewrecursiveDescription]?|?|??|?|??|??|(layer)?|?|暂时刷新界面UI在这个demo中,一开始按钮的背景色是blueColor。现在我们需要在调试的时候把背景颜色改成红色,刷新界面。执行如下命令行,App界面按钮背景色为:(lldb)e((UIButton*)sender).backgroundColor=[UIColorredColor](UICachedDeviceRGBColor*)$41=0x00007fdd10715b00(lldb)e(void)[CATransactionflush]执行上面的命令后,App界面按钮的背景颜色为:这种做法非常好用。我们在调试UI的时候,由于颜色相似,不容易区分,但是我们可以在调试的时候通过这种方式修改背景色,这样就不需要源码重新写相应的代码了运行看看效果。在debug下运行上面的命令后,按钮的背景色变成了红色!***写这篇文章的主要目的是希望小弟们对调试了解不多。写这篇文章是为了帮助小徒弟,也帮助大家在开发中更好的学习调试代码。其实调试命令有很多,但是并不常用,这里就不一一列举了。想了解更多可以进入help查看!
