iOS Xcode调试技巧总结
剑未配好,出门已是江湖。
最近一直没有更新简书是因为在开发和测试阶段,有任务,没有进行学习,不过在做任务的时候也遇到了一些技术点,在这里总结一下。
今天我们来谈一谈�Xcode调试的技巧。�就像玩游戏,有些玩家他们知道怎么操作,会放技能会走路,但是他们不知道买装备,玩了一局下来,鞋子小刀都没有买,这样行走江湖很危险啊!所以我们出门要把装配佩戴好,学会装备自己才是王道!
本文将简书和各大博客上边涉及该方面的内容进行汇总,并试图进行全方位的总结,如果有什么不足之处还希望大家给提出来,我会进行补充和修改。现在我们开始吧。
总体看来,关于调试的方法包括以下几个:�日志输出&LLDB、断点、性能、一些小技巧等几个大的方面。我们一一进行学习和总结。
尝试接受新鲜事物和方法,方法都是熟能生巧的,各种方法综合运用,用好了会事半功倍。
1、�日志输出&LLDB
关于日志输出,我们最先想到的是NSLog,但是弊端在于我们需要在想要打印的位置添加NSLog代码并重新运行项目,这样耽误时间,所以我们平时用的比较多的是打断点,然后po一下。这个“po”就是LLDB里面的一句命令。
那么,什么是LLDB呢:它是一个有着 REPL 的特性和 C++ ,Python 插件的开源调试器。LLDB 绑定在 Xcode 内部,存在于主窗口底部的控制台中。调试器允许你在程序运行的特定时暂停它,你可以查看变量的值,执行自定的指令,并且按照你所认为合适的步骤来操作程序的进展。我们可以简单的理解成它是一个调试器。
(1)LLDB命令行
像下图中,我们打断点后,控制台右边里面会出现一个“lldb”,我们平时不怎么关注它,但我们一直在使用它。

<1> help命令
断点的时候,我们在控制台右边lldb后边输入一个“help”,然后敲回车,就会看到所有关于lldb的命令以及各自的介绍,如下图:


<2> print命令
print很好理解,就是打印,使用过程中我们可以直接用p来代替print。
<3> expression命令
该命令可以改变程序实际参数的值,目的是方便了调试:不用重新运行项目。例如下图中,我们简单的做一个测试,令苹果=1,橘子=2,all应该=3,在断点过程中,我们用expression命令修改了橘子的值,令橘子=5,结果再打印all的时候,all=6(亲测好使)。使用过程中我们可以直接用 expr来代替expression。

这里我们注意到一个“$9”,这里的9是我们使用lldb命令的次数,例如下图,我们expression一次橘子,po了一次all,再print橘子的时候,显示的是“$11”,说明我们print命令是$10。这个不用管,只是提醒我们一下而已,作用应该不是很大。

<4> po命令
现在我们来看看平时用的比较多的“po”,它是“print object”的简写。po一下,我们就可以看到对象的详细信息。po使用的比较多,使用起来也比较简单、方便,这里不做多余的介绍。
<5> image命令
image list 查看工程中使用的库
image lookup --address 0x000000010e0979ac 程序崩溃的时候定位,查看具体报错位置
这个其实我们可以想办法用在我们崩溃日志的收集里面,这样的话我们就可以直接定位到崩溃信息的具体位置了。(亲测,不好使。感觉xcode反馈的崩溃信息不准确。)
<6> call命令
call即调用的意思。上述的po和p也有调用的功能。所以一般只在不需要显示输出,或方法无返回值的时候使用call。 和上面的命令一样,我们依然在viewDidLoad:里面设置断点,然后在程序中断的时候输入下面的命令:
call [self.view setBackgroundColor:[UIColor redColor]]
继续运行程序,看看view的背景颜色是不是变成红色的了!在调试的时候灵活运用call命令可以起到事半功倍的作用(亲测好使)。
(2)LLDB调试栏

一般的按钮和功能我们用的比较多,也比较熟悉,这里我们着重介绍一下Debug View Hierachy和xcode8新增的memory graph功能。
<1>Debug View Hierachy

Debug View Hierachy,翻译过来就是:调试视图层次。除了点击控制台出的图标,也可以从菜单中选择Debug > View Debugging > Capture View Hierarchy 来启动视图调试。(我们可以看出xcode开发人员的用心之处:重叠在一起的长方形,我们大概就明白这个按钮是表示层级关系的)在断点或者不是断点的情况下都可以通过点击这个按钮查看视图层级关系。点击按钮,我们会在xcode最顶端的地方看到下图的一个信息:

capture user interface for YourAppName:capture是捕获的意思,interface,face我们知道是脸,inter是进入的意思,interface就是进入脸,我们大概能够明白这句话的意思就是“为你的app捕获用户交界面”。

从左到右控件排序:(上图中也简单解释了各个功能)
调整视图间距:调整不同视图间的间距。
展示被剪切的内容:当前展示视图中被剪切的部分。
展示约束:展示选中视图的约束。
重置查看区域:将3D渲染透视图恢复至默认状态。
调整查看模式:选择性地展示3D渲染透视图,比如仅展示内容,仅展示框架以及同时展示内容和框架。
缩小:缩小3D渲染透视图
恢复:将3D渲染透视图恢复至默认尺寸。
放大:放大3D渲染透视图
调整可视视图范围:隐藏视图或展示视图,一步步解析3D渲染视图,向左或者向右滑动滑块儿有相反的效果。
有了这个图层关系,我们可以很清楚的知道页面上边的各个控件的位置关系,因为我们在开发阶段�尤其是测试阶段,某个控件上边的字不显示,或者控件的字被遮挡,我们可以用视图调试器查看,是否控件是frame设置的不合理。
<2>memory graph
【经验1】
这个是xcode8新增的功能,翻译过来的意思就是:内存图。有了内存图我们就可以解决闭包引用循环问题了。举个栗子,我们写个循环引用,如下:



(说实在的,这几个命令我在终端不知道怎么调用,试了半天,还是没有搞出来,应该就是内存图调试的树状结构。如果有谁在终端里面知道怎么搞出来,烦请告诉我一下具体怎么操作,谢谢了!)所以,这里我们直接看这个memory graph按钮点击后的效果。
【经验2】
说明1:这个功能是xcode8新增的功能,那么xcode7上边肯定找不到!而xcode8还要10.11.5以上的系统,所以,建议大家先把升级电脑系统,然后安装xcode8。
说明2: 真机的话还需要iOS9或者10的系统。
说明3:查找当前默认Xcode.app的developer路径---终端命令行:xcode-select -p。
如果安装了多个版本的xcode工具,可以使用xcode-select命令指定命令行指令使用哪个版本xcode下的developer目录下的调试工具,即修改路径:xcode-select --switch /Applications/Xcode2.app/Contents/Developer。
踩过的坑:
<1>本来我用的是xcode7.2,挺好用,结果在xcode7上边显然没有这个按钮,升级到xcode8吧。
<2>同事airdrop传来的xcode8.0和xcode8.1,结果提示安装不上,需要升级电脑的系统。
<3>升级好了系统,安装好了同事airdrop传来的xcode8.0不显示。(!准备开始抓狂 !)难道需要8.1才行?!
<4>安装好了同事airdrop传来的xcode8.1,依旧不显示。(!!!抓狂!!!)难道是�打开xcode时候的路径不对?!
<5>修改打开xcode的路径为xcode8.1,依旧不显示。(早已经料到是这个结果了)难道是xcode安装的太多了?!
<6>卸载掉xcode7.0.1、7.2、7.3、8.0,只剩下一个8.1,依旧不显示。(淡定的接受这个结果)难道是同事传的xcode包有问题?!在App Store上自己下载!!
<7>�下载好了,安装好了,依旧不显示。(�生无可恋了。。。)
就在此时,我不知道是被逼疯了还是灵光一闪,拿了同事的真机运行项目,结果居然有了memory graph按钮!!!!
当你认为最困难的时候,恰恰也就是你最接近成功的时候!
当你放弃的时候,你永远不会知道你离成功是那么的接近!
成功很简单,就是在你坚持不住的时候,再坚持一下!
所以建议使用memory graph功能之前确保:
升级电脑系统并安装xcode8.1。(8.1比8.0更稳定)(这里其实并不非得是App Store自己下载,别人用airdrop传的也行)
真机iOS系统在9.0以上。
【经验3】
当你点击这个按钮以后,xcode上边的状态条会显示下图:

Capturing memory graph for YourAppName:翻译过来就是正在捕捉你的app内存图。
来来来,我们先来看看这狗东西......不是,是点击后的效果图😂😂😂

我想说,这是啥玩意?!

【经验4】
在解释上边图结构和各个图标表示的含义之前我们先来看看这几个叹号分别代表什么。

红色叹号:这个最常见,Error=错误。
黄色叹号:这个也常见,Warning=警告。
紫色叹号:这个不常见,不过使用了memory graph我们就会经常见到,Runtime Issue=运行时问题。
蓝色箭头:这个是检查内存泄露是见到的,静态分析Command+Shift+B就可以看见。Static Analyzer Issue=静态分析的问题。
红色叉叉:这个实在instruments里面Leaked用法的时候见到过。UI Test Failed=UI测试失败。
在memory graph状态下我们点击左边的叹号,就会看到RunTime Issue:

【经验5】
在解决这个紫色叹号之前我们先来看看右边灰色三棱锥、蓝色正方体、绿色圆圈等等都代表的是什么。
绿色的一般都是 UIKit 控件及其子类
蓝色一般 NSObject 类及其子类
黄色一般都是容器类型及其子类
灰色括号是指 block
当然还有很多一些其他的类型,具体的大家去看右上角的 Memory Inspect 界面就好,上面都会有详细的信息。
【经验6】
当我们在某一个块上边点击右键的时候,会弹出一个选项框,里面有5个选项:


Quick Look:快速查看,和上图中的小眼睛功能一样。
Print Description:打印详细信息,和上图小眼睛右边的按钮功能一样。
Jump To Definition:跳至代码区。
Reveal in Debug Navigator:在左边的内存树状结构中标蓝色。
Focus on Node:在节点上关注,点击后只剩下跟自己前后箭头相关的节点node。
一开始进入首页的时候,只展示tabbar上边的四个controller。而当我们想要看某个页面的memory graph的时候,我们需要在真机上边跳到那个页面,然后再点击memory graph按钮,才能在左边的树状结构中找到想要看的页面的controller。
【经验7】
和红色、黄色叹号一样,紫色叹号出来了,我们就要想办法解决掉,那么我们看看项目中的紫色叹号都标记在哪里了呢?

各种Reachability啊!!!好不容易找到这个memory graph按钮,好不容易看明白点了内存图,好不容易找到了紫色叹号,你踏玛德告诉我这个错误是因为苹果自己的Reachability造成的!!!(生无可恋。。。。。。。。。。。。。。。。。)紫着吧。
2、断点
断点里面根据作用和功能也有很多种类:普通断点、条件断点、异常断点、符号断点等。我们一一学习介绍。
(1)普通断点(不带技能就出去闯荡江湖的)
当程序运行到断点处时会暂停运行。比如断点打在11行,那么程序就会停在11行(注意:程序只运行到了前10行,第11行其实还没有被执行。)。只要在代码行旁边点击,就能添加一个断点,再次点击,断点变成浅蓝色,就能让断点不可用(disable了,仍然存在,只是不起作用了)。

(2)条件断点(带上技能再闯荡江湖)
打上断点之后,对断点进行编辑,设置相应过滤条件。单击右键会弹出选项框:

Edit BreakPoint... :编辑断点。
Disable BreakPoint :断点失效。(相当于上边说到的单击断点变成浅蓝色,断点失效)
Delete BreakPoint :删除断点。
Reveal in BreakPoint Navigator :在左边的断点树状结构表明该断点。
这里我们主要用到的是第一个:Edit BreakPoint。这里面设置断点的筛选条件(双击断点也可以快速进入编辑断点的对话框)。

【1】Condition:返回一个布尔值,当布尔值为真触发断点,一般里面我们可以写一个表达式。
【2】Ignore: 忽略前N次断点,到N+1次再触发断点。
【3】Action: 断点触发事件,分为六种:
<1> AppleScript:执行脚本。
<2> Capture GPU Frame:用于OpenGL ES调试,捕获断点处GPU当前绘制帧。
<3> Debugger Command:和控制台中输入LLDB调试命令一致。
<4> Log Message:输出自定义格式信息至控制台。
<5> Shell Command:接收命令文件及相应参数列表,Shell Command是异步执行的,只有勾选“Wait until done”才会等待Shell命令执行完在执行调试。
<6> Sound:断点触发时播放声音。
【4】Options(Automatically continue after evaluating actions选项):选中后,表示断点不会终止程序的运行。
【1】Condition:这里我设置i==6,我们看LLDB控制台打印结果:

这里打印了0-5,然后断点断了。这样做的目的就是我们不用在循环里面一个一个的点击下一步,直接跳至我们想要看到的那一步。
【2】Ignore:这里我把Condition的条件取消,设置Ignor的条件为3,我们看LLDB控制台打印结果:

结果是将0-2的循环直接忽略,而后边的循环依旧每次在断点的位置断一次。
(3) 异常断点 Exception Breakpoint(全局断点)
异常断点可以快速定位不满足特定条件的异常,比如常见的数组越界,这时候很难通过异常信息定位到错误所在位置。这个时候异常断点就可以发挥作用了。
添加异常断点:

同样的,全局断点也是可以编辑的,单击右键或者双击断点就会弹出编辑框,编辑的项目和上述是一样的。
(4)符号断点 Symbolic Breakpoint
符号断点的创建也同异常断点。一般符号断点可以在你指定的[类名 方法名]时中断执行。

3、性能检测:
(1)静态分析:通过对代码静态分析,找出代码潜在的错误,如内存泄漏、空引用、未使用函数等。
方法:菜单“Product"->"Analyze"或者Shift+Command+B,然后想办法消灭蓝箭头。
(2)动态分析:通过Instruments工具跟踪分析程序运行时的数据
方法:参考《Instruments性能检测》
4、其他小技巧
(1)模拟器调试:FPS
在《Instruments性能检测》一文中我们就介绍了FPS=Frame Per Sencond:�一秒钟渲染多少帧。
根据苹果全球开发者大会WWDC的说法,当FPS低于45时,用户就会察觉滑动有卡顿。
编译并运行应用程序,选中模拟器,从 Debug菜单中选择Color Blended Layers选项。


红色:表示这些layer是透明的,系统在渲染这些像素点的时候,需要将该view及view一下的其他view混合之后才能得到实际的颜色。红色越深,表明系统在渲染的时候越费劲。
绿色:表示这些layer是不透明的,易于渲染。
黄色:表示这里的点无法直接绘制在屏幕上,此时系统需要对相邻的像素点做反锯齿计算,增加了图形负担。产生的原因是这个控件的背景是通过图片拉伸得到的。
所以推荐尽可能地使用不透明的图层。
(2) 真机调试:截图。
当我们在模拟器上边运行项目的时候,想要给产品或者测试人员看一下页面效果如何,qq截图就可以了,如果在真机上呢怎么截图呢?一般我们会拿着真机给产品或者测试人员看看,但是如果来回折腾很麻烦,我们也可以用自己的手机照相然后发图片给他们,这里还有一个更好的办法对真机进行截图:运行项目,选择Debug--View Debugging--Take Screenshot of 真机名字,这样在你的电脑桌面上边就会有一张你的真机上边选好页面的截图。

参考:1、iOS各种调试技巧豪华套餐
3、Xcode8调试黑科技:Memory Graph实战解决闭包循环引用问题
5、Xcode8的Debug新特性---WWDC 2016 Session 410 & 412 学习笔记
最后,哪里不对的地方可以给我留言,我会及时改进的,谢谢大家。