iOS开发那些事--性能优化–内存泄露问题的解决

简介: <p><strong>内存泄漏问题的解决</strong></p> <p>内存泄漏(Memory Leaks)是当一个对象或变量在使用完成后没有释放掉,这个对象一直占有着这块内存,直到应用停止。如果这种对象过多内存就会耗尽,其它的应用就无法运行。这个问题在C++、C和Objective-C的MRR中是比较普遍的问题。</p> <p>在Objective-C中释放对象的内存是发送rele

内存泄漏问题的解决

内存泄漏(Memory Leaks)是当一个对象或变量在使用完成后没有释放掉,这个对象一直占有着这块内存,直到应用停止。如果这种对象过多内存就会耗尽,其它的应用就无法运行。这个问题在C++、C和Objective-C的MRR中是比较普遍的问题。

在Objective-C中释放对象的内存是发送release和autorelease消息,它们都是可以将引用计数减1,当为引用计数为0时候,release消息会使对象立刻释放,autorelease消息会使对象放入内存释放池中延迟释放。

上代码:

- (void)viewDidLoad

{

[super viewDidLoad];

NSBundle *bundle = [NSBundle mainBundle];

NSString *plistPath = [bundle pathForResource:@"team"

ofType:@"plist"];

//获取属性列表文件中的全部数据

self.listTeams = [[NSArray alloc] initWithContentsOfFile:plistPath];

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

static NSString *CellIdentifier = @”CellIdentifier”;

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {

cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];

}

NSUInteger row = [indexPath row];

NSDictionary *rowDict = [self.listTeams objectAtIndex:row];

cell.textLabel.text =  [rowDict objectForKey:@"name"];

NSString *imagePath = [rowDict objectForKey:@"image"];

imagePath = [imagePath stringByAppendingString:@".png"];

cell.imageView.image = [UIImage imageNamed:imagePath];

cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

return cell;

}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

{

NSUInteger row = [indexPath row];

NSDictionary *rowDict = [self.listTeams objectAtIndex:row];

NSString *rowValue  =  [rowDict objectForKey:@"name"];

NSString *message = [[NSString alloc] initWithFormat:@”您选择了%@队。”, rowValue];

UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@”请选择球队”

message:message

delegate:self

cancelButtonTitle:@”Ok”

otherButtonTitles:nil];

[alert show];

[tableView deselectRowAtIndexPath:indexPath animated:YES];

}


大家看看上面的3个方法会有什么问题呢?如果代码是基于ARC的是没有问题的,遗憾的是基于MRR,上面的代码都存在内存泄漏的可能性。理论上讲内 存泄漏是对象或变量没有释放引起的,但实践证明并非所有的未释放对象或变量都会导致内存泄漏,这与硬件环境和操作系统环境有关,因此我们需要检测工具帮助 我们找到这些“泄漏点”。

在Xcode中提供了两种工具帮助查找泄漏点:Analyze和Profile,Analyze是静态分析工具可以通过菜单 Product→Analyze启动,为静态分析之后的代码画面;Profile是动态分析工具,这个工具叫“Instruments”,它是Xcode 集成在一起,可以在Xcode中通过菜单Product→Profile启动,Instruments有很多Trace Template(跟踪模板)可以动态分析和跟踪内存、CPU和文件系统。

12

我们可以两个工具结合使用查找泄漏点,先使用Analyze静态分析查找可疑泄漏点,再用Profile动态分析中的Leaks和Allocations跟踪模板进行动态跟踪分析,确认这些点是否泄漏,或者是否有新的泄漏出现等。

其中的线段表明了程序执行的路径,在这个路径中,1:说明在25行Objective-C对象引用计数是1,说明在这里创建了一个 Objective-C对象;2:说明在27行引用计数为1这个,该对象没有释放,怀疑有泄漏。这样的说明已经很明显的告诉我们问题所在了, [[NSArray alloc] initWithContentsOfFile:plistPath]创建了一个对象,并赋值给 listTeams属性所代表的成员变量,然而完成了赋值工作之后,创建的对象并没有显示地发送release和autorelease消息。代码修改

NSArray *array = [[NSArray alloc] initWithContentsOfFile:plistPath];

self.listTeams = array;

[array release];


我们看一下tableView:cellForRowAtIndexPath:方法中的疑似泄漏点行末尾的蓝色图标展开分析结果

3

其中主要是说明UITableViewCell*类型的cell对象在64行有可能存在泄漏。在表视图中 tableView:cellForRowAtIndexPath:方法是为表视图单元格实例化并设置数据的,因此cell对象实例化后不能马上 release,应该使用autorelease延迟释放。可以在创建cell对象的时候发送autorelease消息,代码修改如下:

if (cell == nil) {

cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

}


我们看一下tableView:didSelectRowAtIndexPath:方法中的疑似泄漏点有两个,行末尾的图标展开分析结果。

 4

 message对象创建之后没有释放,我们只需要在[alert show]之后添加[message release]语句代码就可以了。在Objective-C中实例化对象有两种方式:

NSString *message = [[NSString alloc] initWithFormat:@”您选择了%@队。”, rowValue];                             ①

NSString *message = [NSString stringWithFormat:@"您选择了%@队。", rowValue];                           ②


①行所示以init-开头构造方法,它的是在alloc之后调用该方法我们称为“实例构造方法”,该方法创建对象所有权是调用者,调用者需要对它的 生命周期负责,具体说负责创建和释放。而另一种是②行所示string-(去掉NS后类名)开头方法,它是通过类直接调用我们称为“类级构造方法”,该方 法是创建的对象所有权非调用者所有,调用者不无权释放它,否则就会因过渡释放而“僵尸化”。

UIAlertView*类型alert对象创建之后没有释放,我们只需要在[alert show]之后添加[alert release]语句代码就可以了,修改之后的代码

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

{

NSUInteger row = [indexPath row];

NSDictionary *rowDict = [self.listTeams objectAtIndex:row];

NSString *rowValue  =  [rowDict objectForKey:@"name"];

NSString *message = [[NSString alloc] initWithFormat:@”您选择了%@队。”, rowValue];

UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@”请选择球队”

message:message

delegate:self

cancelButtonTitle:@”Ok”

otherButtonTitles:nil];

[alert show];

[alert release];

[message release];

[tableView deselectRowAtIndexPath:indexPath animated:YES];

}


上面介绍的使用Analyze静态分析查找可疑泄漏点,之所以称为“可疑泄漏点”,但是这些点未必一定泄漏,确认这些点是否泄漏还要通过 Profile动态分析工具Instruments中的Leaks和Allocations跟踪模板,Analyze静态分析只是一个理论上的预测过程。 通过菜单Product→Profile启动, Profile动态分析工具中选择Leaks模板

5

Instruments中虽然是选择了Leaks模板,但默认情况也会添加Allocations模板,基本上凡是分析内存都会使用 Allocations模板,它可以监控内存分布情况,选中Allocations模板(图中①区域),右边③区域会显示随着时间的变化内存使用折线图 表,同时在④区域会显示内存使用的详细信息,其中刚刚对象分配情况。点击Leaks模板(图中②区域),可以查看内存泄漏情况,如果在③区域有红线出现, 则有内存泄漏,④区域会显示泄漏的对象。

6

出现的泄漏是在点击表视图中单元格测试tableView:didSelectRowAtIndexPath:方法方法时候发生的,其中 NSCFString类型的对象发生了泄漏,NSCFString类型在NSFoundation中是NSString*类型。点击泄漏对象前面的三角形 展开对象,可以看到它们的内存地址、占用字节、所属框架和响应方法信息。

7 打开扩展详细视图,可以看到右边的跟踪堆栈信息,其中我们自己应用代码,可以点击进入我们程序代码,会打开对应代码。 8

代码77并不是泄漏点,而是其中的NSString*类型对象在之后发生了泄漏,因此可以断定是message对象之后没有释放导致泄漏。我们修改代码如下:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

{

NSUInteger row = [indexPath row];

NSDictionary *rowDict = [self.listTeams objectAtIndex:row];

NSString *rowValue  =  [rowDict objectForKey:@"name"];

NSString *message = [[NSString alloc] initWithFormat:@”您选择了%@队。”, rowValue];

UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@”请选择球队”

message:message

delegate:self

cancelButtonTitle:@”Ok”

otherButtonTitles:nil];

[alert show];

    [message release];

[tableView deselectRowAtIndexPath:indexPath animated:YES];

}


添加[message release]语句。很多人还会猜测alert对象(UIAlertView*)会有泄漏,因此重新运行Instruments工具,反复点击单元格测 试,并未发现表示内存泄漏的红线! Instruments工具认为alert对象不释放不会引起内存泄漏,如果我们想进一步评估它对于内存的应用,这个时候我们可以看看 Allocations模板的折线图表,每次点击总占用内存数都有所增加,这说明alert对象没有释放虽然不是很严重,但是也会增加占用内存,因此 alert对象释放也是必须的。

9

这就是我们介绍的内存泄漏问题解决方法,事实上内存泄漏是极其复杂问题,工具使用是一方面,经验是另一方面。提高经验,然后借助于工具才是解决内存泄漏的根本。


目录
相关文章
|
8月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
792 4
|
6月前
|
iOS开发 开发者
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
362 67
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
|
5月前
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
140 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
7月前
|
iOS开发 开发者 MacOS
深入探索iOS开发中的SwiftUI框架
【10月更文挑战第21天】 本文将带领读者深入了解Apple最新推出的SwiftUI框架,这一革命性的用户界面构建工具为iOS开发者提供了一种声明式、高效且直观的方式来创建复杂的用户界面。通过分析SwiftUI的核心概念、主要特性以及在实际项目中的应用示例,我们将展示如何利用SwiftUI简化UI代码,提高开发效率,并保持应用程序的高性能和响应性。无论你是iOS开发的新手还是有经验的开发者,本文都将为你提供宝贵的见解和实用的指导。
241 66
|
5月前
|
人工智能 程序员 API
iOS|记一名 iOS 开发新手的前两次 App 审核经历
啥,这玩意也有新手保护期?
87 0
|
7月前
|
存储 监控 API
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
616 11
|
7月前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
274 3
|
7月前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
8月前
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
190 6
|
8月前
|
存储 前端开发 Swift
探索iOS开发:从新手到专家的旅程
本文将带您领略iOS开发的奇妙之旅,从基础概念的理解到高级技巧的掌握,逐步深入iOS的世界。文章不仅分享技术知识,还鼓励读者在编程之路上保持好奇心和创新精神,实现个人成长与技术突破。

热门文章

最新文章