内存泄漏问题的解决
内存泄漏(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和文件系统。
我们可以两个工具结合使用查找泄漏点,先使用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:方法中的疑似泄漏点行末尾的蓝色图标展开分析结果
其 中主要是说明UITableViewCell*类型的cell对象在64行有可能存在泄漏。在表视图中 tableView:cellForRowAtIndexPath:方法是为表视图单元格实例化并设置数据的,因此cell对象实例化后不能马上 release,应该使用autorelease延迟释放。可以在创建cell对象的时候发送autorelease消息,代码修改如下:
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
我们看一下tableView:didSelectRowAtIndexPath:方法中的疑似泄漏点有两个,行末尾的图标展开分析结果。
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模板
Instruments 中虽然是选择了Leaks模板,但默认情况也会添加Allocations模板,基本上凡是分析内存都会使用 Allocations模板,它可以监控内存分布情况,选中Allocations模板(图中①区域),右边③区域会显示随着时间的变化内存使用折线图 表,同时在④区域会显示内存使用的详细信息,其中刚刚对象分配情况。点击Leaks模板(图中②区域),可以查看内存泄漏情况,如果在③区域有红线出现, 则有内存泄漏,④区域会显示泄漏的对象。
出 现的泄漏是在点击表视图中单元格测试tableView:didSelectRowAtIndexPath:方法方法时候发生的,其中 NSCFString类型的对象发生了泄漏,NSCFString类型在NSFoundation中是NSString*类型。点击泄漏对象前面的三角形 展开对象,可以看到它们的内存地址、占用字节、所属框架和响应方法信息。
打开扩展详细视图,可以看到右边的跟踪堆栈信息,其中我们自己应用代码,可以点击进入我们程序代码,会打开对应代码。
代码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对象释放也是必须的。
这就是我们介绍的内存泄漏问题解决方法,事实上内存泄漏是极其复杂问题,工具使用是一方面,经验是另一方面。提高经验,然后借助于工具才是解决内存泄漏的根本。
相关推荐
映客直播iOS-App-性能优化.pdf
资源名称:iOS开发视频教程资源目录:【】iOS开发视频教程-第01讲-iOS历史介绍【】iOS开发视频教程-第02讲-XCode安装【】iOS开发视频教程-第03讲-UIView_PPT【】iOS开发视频教程-第04讲-UILabel【】iOS开发视频教程-...
总的来说,"iOS开发进阶篇-成为一个iOS开发高手"涵盖了从语言特性的深度挖掘,到应用架构设计、系统服务的利用,再到性能优化和新技术的应用,是一份全面且深入的学习资源。通过系统学习并实践其中的知识,开发者...
通过学习和实践这个源码包中的内容,你可以提升自己的iOS开发技能,解决实际问题,并为成为iOS开发高手奠定坚实基础。每个知识点都值得深入研究,结合实践,你将能够更好地理解和掌握iOS开发的核心技术。
iOS-Performance-Optimization:iOS 性能优化专题合集.zip,关于iOS 性能优化梳理、内存泄露、卡顿、网络、GPU、电量、 App 包体积瘦身、启动速度优化等、Instruments 高级技巧、常见的优化技能- Get — Edit
iOS性能优化 iOS是苹果公司开发的移动操作系统,广泛应用于iPhone、iPad、iPod touch等苹果设备上。随着移动设备的普及,为移动设备提供流畅稳定用户体验的重要性日益凸显,而性能优化是实现这一目标的关键步骤。...
根据提供的信息,我们可以推断出这是一本关于iOS开发进阶的书籍,作者为唐巧。虽然提供的部分内容似乎并不是实际的章节内容,但从标题、描述和标签中,我们可以推测本书可能涵盖的一些关键知识点。 ### iOS开发进阶...
搞了好久在网上搜了很多资料都不能解决最后找到了一个c/c++的底层源码,才实现了最后整理了下上传了。是个ios的demo很详细你定能看懂的。运行在xcode环境下,关键算法的文件是AES.h和AES.mm文件。
【Object-C】ios苹果app开发入门object-c例子demo14个实例 一般编程问题 【实例简介】 ios苹果app开发入门,object-c,例子,demo,14个实例 初学者难得的学习实例 【实例截图】 【核心代码】 归档 ├── Air...
这个压缩包"IOS应用源码Demo-获取当前硬件可用内存的例子UIDevice-毕设学习.zip"提供了一个实例,教你如何利用`UIDevice`类来获取设备的可用内存信息,这对于进行性能优化和调试非常有用。下面将详细介绍这一知识点...
在手淘iOS应用的开发过程中,性能优化是一项至关重要的任务,尤其在面对日益增长的用户需求和市场竞争时。本文将深入探讨手淘团队在iOS性能优化上的实践和策略,以及如何通过研发流程、性能度量和监控来确保应用的...
《iOS底层原理内存管理性能优化》是一门深入探讨iOS应用开发中关键性能优化技术的课程。这门课程由李明杰老师主讲,主要涵盖了Objective-C语法、多线程、Runtime、内存管理、Runloop、性能优化以及设计模式与架构等...
在iOS应用开发中,性能优化是一项至关重要的任务,它直接影响着用户体验和应用的市场竞争力。本文将基于"swift-iOS性能优化总结"的主题,详细探讨内存管理、UI流畅性、网络请求、GPU优化、电量消耗控制、App包体积...
6. **调试与优化**:利用Nordic的nRF Connect SDK或类似工具进行调试,优化代码性能和用户体验。 总的来说,iOS-nRF-Mesh-Library为开发者提供了强大的工具,使得在iOS平台上构建蓝牙Mesh应用变得更为便捷,极大地...
OOMDetector是一个iOS内存监控组件,应用此组件可以帮助你轻松实现OOM监控、大内存分配监控、内存泄漏检测等功能。 特性 1.OOM监控:监控OOM,Dump引起爆内存的堆栈 2.大内存分配监控:监控单次大块内存分配,提供...
"location-cleaned"驱动作为iOS 15.0中的一个重要组成部分,它对于优化定位性能,提升隐私保护起到了关键作用。本文将深入探讨这一技术,帮助读者理解其工作原理和应用。 一、iOS 15.0定位服务概述 iOS的定位服务...
8. **性能优化**:针对iOS设备的特性,源码可能涉及到内存管理、帧率优化等技巧,以确保游戏在各种设备上流畅运行。 9. **版本控制**:文件名中的“cba07f8”可能是Git版本控制系统中的一个哈希值,表明这个源代码...
在iOS开发中,加载大图或过多图片可能会导致应用程序内存飙升,从而引发性能问题甚至App崩溃。本篇文章将深入探讨如何解决"ios加载图片内存暴涨"的问题,主要介绍三种策略:第一种是使用UIKit的`setImage`方法,第二...