当前位置: 首页 > 科技观察

iOS内存管理:内存优化

时间:2023-03-13 09:20:04 科技观察

所谓内存优化,在程序设计的过程中,在保证程序运行效率的前提下,尽量压缩程序运行时占用的内存.不管硬件设备有多少内存,程序运行时占用的内存越少越好。下面我将介绍一些在项目开发过程中优化内存的方法。一、关于UITableView在项目开发中,UITableView是用的比较多的一个视图控件。如果能优化UITableView的使用,程序的性能会提高很多。(1)善于使用UITableViewCell的复用机制Reuse机制:在这种机制下,系统默认有一个变量数组NSMutableArray*visibleCells,用来保存当前显示的cell。可变字典NSMutableDictnery*reusableTableCells,用于存储可重用的单元格。UITableView只会创建一个单元格屏幕并将它们放在visibleCells中。每当一个单元格滑出屏幕时,它就会被放入reusableTableCells中。当某个位置的cell要显示时,先去reusableTableCells中获取。有则直接使用;如果没有,它将被创建。这大大减少了内存开销。iOS6之后,除了可以复用UITableView和UICollectionView中的cell,还可以复用各个Section的Header和Footer。可见苹果一直在不断优化。在项目开发中,我们需要为UITableViewCells、UICollectionViewCells、UITableViewHeaderFooterViews设置正确的reuseIdentifier。当需要复用多种类型的cell时,我们可以根据reuseIdentifier来区分。我们可以在Xcode中设置,如下图所示:下面是一个cell复用的简单例子:*cell=nil;cellIdentifier=@"reuseIdentifiermarkedinyourxibfileview";cell=[tableViewdequeueReusableCellWithIdentifier:cellIdentifier];//根据标识符重用cell//如果没有对应的cell,则创建cellif(!cell){cell=[[UITableViewCellalloc]initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:cellIdentifier];}returncell;}复用cells是一个很好的机制,但是使用不当也会出现问题,就是所谓的复用重叠问题。看下面的代码:我本来打算把偶数行设置为蓝色,基础行设置为默认颜色,单元格的内容设置为行数来区分。结果如图:从上图可以看出,最初初始化的13~14个cell是正常的,但是当tableview滑动的时候,出现了问题,一些baserowcell也变成了蓝色。这是因为下面的cells基本都被复用了。当指定单元格的属性不显示时,会使用已经创建的单元格的属性,导致有的蓝有的白。解决方法是这样写:切记:给多个cell赋属性时,一定不能写在if(!cell){}中,以免复用时出现问题。(2)优化UITableViewCell高度计算UITableView有两个很重要的回调方法:tableView:cellForRowAtIndexPath:和tableView:heightForRowAtIndexPath:。很多人认为在初始化tableview时,会先调用前者进行创建,再调用后者进行布局和属性设置。不是这样。真实情况是这样的:UITableView继承自UIScrollView,需要先确定它的contentSize和每个Cell的位置,然后再把复用的Cell放到相应的位置。所以其实UITableView的回调顺序就是多次调用tableView:heightForRowAtIndexPath:确定contentSize和Cell的位置,然后调用tableView:cellForRowAtIndexPath:把Cell显示在当前屏幕上。例如:如果现在要显示20个单元格,则当前屏幕显示5个单元格。那么在刷新(reload)UITableView的时候,UITableView会先调用tableView:heightForRowAtIndexPath:方法20次,然后再调用tableView:cellForRowAtIndexPath:方法5次;滚动屏幕时,每当Cell滚动到屏幕中时,tableView:heightForRowAtIndexPath:,tableView将调用一次:cellForRowAtIndexPath:方法。所以UITableViewCell的高度计算的优化就是对这两个函数的处理。至于怎么优化@我很得吧向尼怎么回事,我写了一篇不错的文章介绍一下。我不会说太多。(3)延迟加载(delayedloading)延迟加载并没有减少程序的内存消耗,而是延迟了对象的加载时间,在对象使用的时候进行初始化。比如一个UITableView一共有20行,但是屏幕只显示了一个5行的数组。然后在初始化tableview的时候,只能先加载5行数据,其他15行显示的时候再加载。这减少了初始化表视图所需的内存。(这么说有点牵强,因为实时加载会影响tableview的流畅度,不过基本就是这个意思><)2.关于图片的处理,图片会在里面占用很大的开销记忆。如果图片处理得当,内存消耗会减少很多。(1)缓存图片从bundle中加载图片有两种常用的方式,一种是使用imageNamed,另一种是使用imageWithContentsOfFile,最后一种比较常用。imageNamed的优点是图像在加载时被缓存。imageNamed的文档是这样说的:此方法在系统缓存中查找具有指定名称的图像对象,如果存在则返回图像对象。如果在缓存中没有找到相应的图像,则此方法从指定的文档中加载并缓存它并返回此对象。也就是说imageNamed方法加载的图片会缓存图片。而imageWithContentsOfFile方法没有。因此,如果要加载的图片比较小,会重复使用,这种情况下选择imageNamed;如果要加载大图,而且是一次性使用,那就用imageWithContentsOfFile,不用浪费内存去缓存。代码示例:(2)调整图片大小我们经常从网络或者本地bundle中获取图片,然后加载到UIImageView中。加载图片时,尽量保证图片的大小与UIImageView的大小一致。因为在运行时缩放图片是非常耗费资源的,如果将UIImageView嵌套在UIScrollView或者UITableView中,会消耗更多的资源。对于从本地bundle加载的图片,我们可以提前对图片进行处理。对于网络下载的图片,下载完成后,我们需要对图片进行缩放,然后加载。(3)代码渲染或直接获取上面说了,使用代码渲染一张图片,会使图片占用的内存增加一倍。但是用代码画图可以很好的控制画面,可以做出很多漂亮的效果,前提是牺牲一部分内存;如果所有图片都从包中加载怎么办?那样会增加bundle的体积,同时无法用代码灵活处理图片的效果。因此,在开发过程中,代码是渲染图像还是从bundle中获取图像之间存在权衡。3、数据处理在项目开发中,我们会用到各种格式的数据,比如JSON、XML等,还有数组、链表、字典、集合等各种数据结构。使用正确的数据格式,使用正确的数据结构会减少我们的资源消耗。(1)选择正确的数据格式App在与网络进行交互时,往往会使用到JSON或XML数据格式。JSON是一种轻量级的数据交换格式,具有良好的可读性和易于快速编写的特点。解析JSON会比XML快,但是JSON传输的数据比较小。XML是一种重量级数据交换格式,适用于非常大的数据传输。当数据量很大时,使用XML数据格式会大大减少内存消耗,提高性能。另外,尽量避免多次转换数据。比如tableview需要以数组的形式赋值。然后服务器尝试返回数组类型。如果返回的是JSON类型,转成NSArray类型也会增加开销。(2)选择正确的数据结构不同的数据结构处理数据的速度不同。ArrayNSArrayNSMutableArray:一组有序的值。使用索引的查询很快,使用值的查找很慢,插入/删除很慢。DictionaryNSDictionaryNSMutableDictionary:存储键值对。使用键查找速度更快。CollectionNSSetNSMutableSet:一组无序的值。按值查找很快,插入/删除也很快。4.视图处理(1)避免使用过于复杂的xibs当前很多项目开发中,经常会用到xibs。加载xib时,所有内容都放在内存中,包括任何图像。如果xib文件太大,会占用大量内存。Xib不同于故事板。即使暂时不使用xib,该视图也会存在于内存中;故事板仅在需要时实例化视图控制器。并且在设置view属性的时候,尽量把opaque属性设置为YES(不透明)。这将改进渲染系统以优化一些渲染过程并提高性能。(2)正确设置View的背景设置UIView的背景图片主要有两种方式:使用UIColor的colorWithPatternImage设置背景颜色;向UIView添加一个UIImageView子视图。第一种方法适用于使用小图像块创建背景,可以更快地渲染并且不会消耗大量内存。例如,使用10x10像素大小来重复背景。第二种方法适合用大图,即整张图来设置背景。如果你使用colorWithPatternImage它会消耗太多内存并且你会收到内存警告并且应用程序会突然崩溃。使用UIImageView将节省大量内存。(3)设置阴影路径如果使用下面的代码给view.layer添加阴影:这会让CoreAnimation不得不在后台绘制图形,在渲染前添加阴影,开销很大。如果使用shadowPath,就会避免这个问题:5.合理使用AutoreleasePoolNSAutoreleasePool负责释放block中的autoreleased对象。通常它会被UIKit自动调用。但在某些情况下也需要手动创建。如果你创建了很多临时对象,你会发现内存一直在减少,直到这些对象被释放。这是因为只有当UIKit用完自动释放池时才会释放内存。但是如果自己定义@autoreleasepool,在里面创建临时对象,就可以避免这个问题:6.正确处理cache缓存可以分为内存缓存和磁盘缓存。在项目开发过程中,我们经常会缓存一些图片、声音、数据。缓存机制的合理使用,将大大提高程序的性能,提高APP的流畅度。比如被广泛使用的SDWebImage,就采用了这样的缓存机制:(1)先查看内存缓存,有则直接获取。(2)如果没有内存,就从磁盘缓存中获取。(3)如果没有磁盘缓存,直接通过URL从网络上下载。当然,这只是简单的描述。更详细的可以参考@南峰子_Laolv的SDWebImage实现分析。合理处理缓存可以提高程序的性能,而不必每次都从网络上获取数据。但是你不能把所有的东西都存储在缓存中,这会消耗大量的内存和磁盘空间。所以要合理使用缓存机制。总结以上就是我对内存优化的理解。在写这篇文章的过程中,参考了很多大牛的文章。作为应届本科生,我对oc的理解还很浅薄。如果有错误或者需要补充的地方,希望大家指出。我会改正并学习。