05iOS崩溃、内存调试技巧

调试地址

【1.普通断点】

当程序运行到断点处时会暂停运行。比如断点打在23行,那么程序就会停在23行(注意:程序只运行到了前22行,第23行其实还没有被执行!!!)。在某一行创建断点的快捷键是:command+

普通断点调试

并能在调试过程中在下方看到参数的值:

调试下的参数

【2.条件断点】

我们还能对断点的属性进行配置,设置条件,使断点更加智能化,右键断点进入编辑对话框:

设置条件.png

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

04.png

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

05.png

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


06.png

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

07.png

调试输出如下:


08.png

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

09.png

输出结果如下:

10.png

【3.异常断点】

开发iOS知道,如果我们因为异常然后程序crash了,代码就直接跑到main.m的main函数中去了。为什么就不能跑到出现异常的代码中呢???异常断点就为我们解决该问题,程序就会在异常出现的那行代码终止。创建异常断点图例如下:

11.png

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


12.png

【4.符号断点Symbolic Breakpoint】

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

13.png

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


14.png

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


15.png

【5.Analyze分析器】

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

1.静态内存分析概念:不运 程序,直接对代码进 内存分析,分析是否有内存泄露

优点:分析速度快,可以快速对所有的代码进 内存分析,查找出来对应的内存泄露

缺点:不定准确,但是基本准确.根据语法上下 来分析你的程序到底有没有内存注意:如果提 有内存泄露, 定要根据上下 语法分析代码是否有问题.

16.png

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


17.png

【6.Profile检查器Leaks】

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

动态内存分析 概念:真正运 起来程序,并且借助 具来分析代码是否有某些地 产 了内存泄露 优点:分析 常准确,并且只要分析出来有内存泄露,基本可以断定代码 定有问题 缺点:需要 处 处来分析,并不能对全局的代码进 分析. 注意:在真实开发中,应该是静态内存分析和动态内存分析结合的 式来分析内存. 特别是ARC环境下 的CoreFoundation框架的东 ,使 静态内存分析先分析,之 后使 动态内存分析再来分析 次,

18.png

【7.僵尸对象】

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

19.png

【8.lldb命令】

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

20.png

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

21.png

【9.NSLog打印】

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

#import "ViewController.h" 

#define NSLog(format, ...) do { \ 
fprintf(stderr, "<%s : %d> %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

打印结果如下:

22.png

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

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

- (instancetype)init 
{ 
    self = [super init]; 
    if (self) { 
        //初始化语句;  
} 
    return self; 
} 
- (void)dealloc 
{ 
        //释放后调用; 
}

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

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

#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

打印结果如下:

23.png

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

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

【13.视图调试】

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

24.png

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

25.png

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

26.png
27.png

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

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

#ifndef __OPTIMIZE__  
    //这里执行的是debug模式下 
else 
    //这里执行的是release模式下 
#endif

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

#if defined (__i386__) || defined (__x86_64__)  

    //模拟器下执行 
#else      
    //真机下执行 
#endif

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

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

【15】预编译宏在开发调试中非常有用,我们来仔细实践一下:

(1)if的预编译命令,根据后面的条件判断是否执行,因为这里条件为1,始终为真,所以在#if...#endif中的代码一定会执行。

#if 1  
    //这里的代码一定会执行
#endif

(2)这里的条件为0,为假,所以#if...#endif里面的代码一定不会执行。
#if 0
//这里的代码一定不会执行
#endif

(3)预编译命令还能根据是否宏定义某个标志,来选择是否执行。
#ifdef MY_TOP
//如果 MY_TOP 被宏定义过,那么里面的代码会执行,否则不会执行。
#endif

(4)预编译命令还可以进行嵌套,就像普通的条件判断一样。
#ifdef MY_TOP
//如果 MY_TOP 被宏定义过,那么里面的代码会执行,否则不会执行。
#if 1
//根据条件判断是否执行
#endif

    #ifdef MY_TOP2  
    //嵌套判断  
    #endif 
#endif

(5)既然有#ifdef...#endif,相反的,也有#ifndef...#endif,#ifndef的作用正好和#ifdef相反,如果宏定义没有被声明,那么将会执行执行;如果宏定义被声明了,就不会执行下面的代码。

#ifndef YOU_TOP  
    //如果 YOU_TOP 没有被声明,就会执行里面的代码,否则不会执行。 
#endif

(6)既然是类似条件判断,那么就不得不说#else,在条件编译中,同样有#if...#else的判断,执行逻辑和普通的条件判断一样。

#ifdef MY_TOP  
    //如果 MY_TOP 被宏定义过,那么这里的代码会执行,否则不会执行。  

    #else  
    //要么执行else外面的,要么执行else里面的。  
    #if 1  
    //根据条件判断是否执行  
    #endif 
#endif

(7)每出现一次if,就一定要有#endif去包裹,否则预编译器会提示你错误。如果有多层的嵌套,不同层次需要有适当的缩进。

(8)对于项目暂时不执行的代码,不建议使用注释代码去禁用。推荐使用预编译命令去打开关闭一段代码。

【16】#warning的使用

有时候在代码中出现大量黄色的警告是非常烦人的事情,大量的警告不利于代码的维护与调试。但是有时候我们需要手动去打一些警告,让我们记住这里应该要注意些什么重要的事情。在code review中,别人也会打一些#warning,提示你应该要注意什么问题,方便我们去查找问题和修改。

28.png

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

http://www.jianshu.com/p/336d643e0390

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容