iOS开发调试技巧总结

原文地址:http://www.cocoachina.com/ios/20160321/15726.html   原文持续更新中

声明:本文是我在看完原文之后,在原文基础上做的一些整理,仅代表个人意见,如有疑问请参考原文。

对于软件开发而言,调试是必须学会的技能,重要性不言而喻。对于调试的技能,基本上是可以迁移的,也就是说你以前在其他平台上掌握的很多调试技巧,很多也是可以用在iOS开发中。不同语言、不同IDE、不同平台的调试,有同性也有个性。今天我们就来学习一下iOS开发中的调试技巧,语言暂用为OC,IDE当然是强大的Xcode。首先说明下,Xcode已经为我们调试项目提供了极大的方便。

【1.普通断点】

该方法很常用,如有不懂,请参考原文。

【2.条件断点】

条件断点:对普通断点的属性进行配置,设置条件,使断点更加智能化。右键断点即可进入断点编辑对话框,如下图:

20160107205738854.png

我以一个循环作为测试代码:

20160107210259173.png

循环中的代码每次都要单步执行,可能这并不是我想要的。我想要在i为3的时候中断程序,进行调试,编写条件如下:

20160107210654311.png

设置i==3的条件后,程序就会在该条件时中断,而不会每次到达该位置都中断。中断时输出如下:

20160107210924740.png

同时也可以设置Ignore参数,会忽略前面n次的断点运行,会在第n+1次中断。

12.png

调试输出如下:

13.png

同时,还可以查看某个函数被调用的次数,设置Action参数如下,注意要选中Automatically continue after evaluating actions.

QQ截图20160318151102.png

输出结果如下:

QQ截图20160318151110.png

【3.异常断点】

异常断点使程序crash时代码就直接定位到出现异常的代码处,而不是默认定位到main.m的main函数中。创建异常断点图例如下:

QQ截图20160318151147.png

如下所示就创建完成了。如果碰到异常crash时,尝试使用异常断点吧。

QQ截图20160318151213.png

【4.符号断点Symbolic Breakpoint】

符号断点的创建也同异常断点。一般符号断点可以在你指定的[类名 方法名]时中断执行。

QQ截图20160318151238.png

配置符号断点如下:可以当执行到ViewController类的viewDidLoad方法时中断执行。

122.png

如果你的Symbol只写了一个函数名,那么就会在出现该函数名的地方就中断执行。如下,就会在运行到doAnimation的时候中断。是不是很强大呢?

QQ截图20160318151313.png

QQ截图20160318151319.png

【5.Analyze分析器】

Analyze分析器是一种静态的工具,可以对我们的程序进行分析,找出我们未使用的变量,或一些死存储。执行Analyze如下:Product-->Analyze. 如下蓝色的标记就是静态分析的结果。

QQ截图20160318151313.png

QQ截图20160318151319.png

当然,我们可以设置在编译程序的时候同时Analyze,把下列选项设为Yes即可。

QQ截图20160318151400.png

【6.Profile检查器】

这个工具实在是太NB了,三言两语说不完,贴张图,大家感受一下,我会在以后的博客中慢慢讲解该工具的使用。同样在Product-->Profile中打开。

QQ截图20160318151533.png

【7.僵尸对象】

iOS中把那些已经release但还没完全消失的对象叫做僵尸对象,对已经release的对象再次释放,就会发生异常。虽然自从使用ARC后,由于对象释放产生的异常已经大大变少,但偶尔还会出现。开启僵尸对象模式后,就能快速定位到异常位置。开启方式如下:Product-->Scheme-->Edit Scheme. 勾选Enable Zombie Objects即可。

001.png

【8.lldb命令】

Xcode中使用llvm编译器,公认为最好的C、C++、OC、Swift编译器。而lldb是llvm中的调试器,我们可以使用一些简单的命令进行调试,我还是把上面的循环代码作为测试代码。

002.png

断点调试中,使用po命令、print命令在Console控制台打印出变量信息:

003.png

【9.NSLog打印】

应该说NSLog打印信息是初学者最喜欢的调试手法,也是最简单的调试,通过打印出的信息查看程序运行的路径。但是打印出的信息较少,本身NSLog效率较低,有人使用宏做了部分优化,代码如下:能够打印出所在类名、所在方法名、详细时间、行号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#import "ViewController.h"  
   
#define NSLog(format, ...) do { \  
fprintf(stderr,  " %s\n" , \  
[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], \  
__LINE__, __func__); \  
(NSLog)((format),  ##__VA_ARGS__); \  
fprintf(stderr,  "-------\n" ); \  
while  (0)  
   
@interface ViewController ()  
  
@end  
   
@implementation ViewController  
   
- (void)viewDidLoad {  
   
     [ super  viewDidLoad];  
     for  (int i = 0; i < 5; i++) {  
         NSLog(@ "我的值:%d" ,i);  
     }  
}  
   
@end

打印结果如下:

005.png

【10.生命周期方法init,dealloc】

对于ViewController来说,有两个生命周期函数我们可以进行重写,也就是init和dealloc方法。对于某些对象的状态,我们可以在这两个方法中查看。尤其是在dealloc中可以看到当ViewController退出的时候某个对象是否release。

1
2
3
4
5
6
7
8
9
10
11
12
13
- (instancetype)init  
{  
     self = [ super  init];  
     if  (self) {  
         //初始化语句;  
     }  
     return  self;  
}  
   
- (void)dealloc  
{  
     //释放后调用;  
}

【11.查看代码运行时间】

有时候我们想要准确的知道某段代码、某个循环执行的时间,然后分析效率等问题,这个时候就需要执行时间是多少。正好看到网上已经有人做了这个工作,我就直接摘下来了。正好也用了宏的方式计算时间,我们只要在需要计算时间的代码块前后写上TICK,TOCK宏即可。当然,原理也是非常的简单,也就是使用NSDate计算差值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#import "ViewController.h"  
   
#define TICK   NSDate *startTime = [NSDate date]  
#define TOCK   NSLog(@"Time: %f", -[startTime timeIntervalSinceNow])  
   
@interface ViewController ()  
   
@end  
   
@implementation ViewController  
   
- (void)viewDidLoad {  
   
     [ super  viewDidLoad];  
   
     TICK;  
     for  (int i = 0; i < 5; i++) {  
         NSLog(@ "我的值:%d" ,i);  
     }  
     TOCK;  
}  
   
@end

打印结果如下:

006.png

【12.真机调试时电脑上映射显示手机屏幕】

当手机接上电脑后,注意要把调试设备选为自己的手机,然后选择Debug-->View Debugging-->Take Screenshot...   .然后可以看到手机屏幕已经在你的电脑桌面了。

007.png


【13.viewDidLoad不建议写太多代码】

个人建议不要在viewDidLoad方法中写入太多代码。尤其是涉及该界面中的动画的时候,因为执行viewDidLoad方法的时候,界面可能还没完全加载出来,如果此时把动画放在viewDidLoad中,可能会造成动画无法显示。当然也不建议把耗时的网络请求和动画效果都放在viewDidLoad中,界面的阻塞也会造成动画无法显示。可以尝试把动画放在viewDidAppear,viewWillAppear方法中。对于这类涉及UI的问题,调试也是比较麻烦的。。。

【14.视图调试(查看界面层次)】

如今iOS开发的UI设计有很多种方式,比如storyboard,xib,代码实现。对于stoayboard,xib可视化实现是比较简单的,但是对于一些“iOS老程序员”而言,都喜欢使用代码实现UI,并且可能UI层次还比较复杂。这样就给我们新接手项目的开发者带来很多困扰。如何快速查看一个复杂UI的界面层次和布局,最快的方法就是用到视图调试。

当项目运行到某一个界面(可以是模拟器或真机)时,开启视图调试,点击按钮如图:

008.png

这样就会进入试图调试,你可以很方便的查看这个界面。这里可以看到控件之间的层次关系。

009.png

左侧的树形层次图可以在查看线程、队列和UI之间切换:

010.png

【15】常用的编译宏定义:可以让代码在不同的编译情况下执行

(1)__OPTIMIZE__  :用于release和debug的判断,当选择了__OPTIMIZE__  时,可以让代码在release时执行,在debug时不执行。示例如下:

1
2
3
4
5
#ifndef __OPTIMIZE__  
     //这里执行的是debug模式下  
else  
     //这里执行的是release模式下  
#endif

(2)__i386__ 与 __x86_64__   :用于模拟器环境和真机环境的判断。满足该条件的代码只在模拟器下执行。示例代码如下:

1
2
3
4
5
6
7
#if defined (__i386__) || defined (__x86_64__)  
   
     //模拟器下执行  
#else  
   
     //真机下执行  
#endif

(3)__IPHONE_OS_VERSION_MAX_ALLOWED  :当前编译的SDK版本,可以与__IPHONE_9_0等宏定义进行比较,进行不同版本下代码的执行。示例如下:

1
2
3
4
5
if  (__IPHONE_OS_VERSION_MAX_ALLOWED == __IPHONE_9_0) {  
         //如果当前SDK版本为9.0是执行这里的代码  
     } else {  
         //否则执行这里  
     }

【16】预编译宏 #ifdef     #else   #endif

1
2
3
4
5
#ifdef ****  
     //代码1  
else  
     //代码2  
#endif

意思是 如果标识符****已被#define命令定义过,则对代码1进行编译,否则对代码2进行编译。

同时也有#ifndef,则与#ifdef刚好相反。

总结

调试不仅仅是我上面提到的技巧,更多的是长年累月积累下来的经验,只有在自己的开发中不断的出错、试错、调错、解决错误的过程中才能提高自己的编程水平和调试能力。我会继续更新该篇博客,讲解更多的调试技能,希望我们都能在实践中提高进步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值