增强iOS应用程序性能方法

1.  使用ARC进行内存管理

ARC除了能避免内存泄露外,还有助于程序性能的提升

2. 在适当的情况下使用reuseIdentifier

为了使用reuseIdentif iers,在tableview请求一个新的cell时,在数据源中调用下面的方法:

staticNSString*CellIdentifier=@"Cell";

UITableViewCell*cell=[tableViewdequeueReusableCellWithIdentifier:CellIdentifier f orIndexPat h:indexPat h];

3. 尽可能将View设置为不透明(Opaque)

opaque属性提示绘制系统如何处理view。如果opaque设置为YES,绘图系统会将view看为完全不透明,这样绘图系统就可以 优化一些绘制操作以提升性能。如果设置为NO,那么绘图系统结合其它内容来处理view。默认情况下,这个属性 是YES。

如果屏幕是静止的,那么这个opaque属性的设置与否不是一个大问题,但是,如果view是嵌入到scroll view中 的,或者是复杂动画的一部分,不设置这个属性的话肯定会影响程序的性能!

4. 避免臃肿的XIBs

如果必须要使用XIBs 的话,尽量让XIBs 文件简单。并且每个view controller对于一个XIB文件,如果可以的话,把一个view controller的view不同的层次单独分到一个XIBs文件中。

当把一个XIB文件加载到内存时,XIB文件中的所有内容都 将被加载到内存中,包括图片。如果有一个view还不立即使用的 话,就会造成内存的浪费。

5. 不要阻塞主线程

永远都不要在主线程做繁重的任务。因为UIKit 的任务都在主线程中进 行,例如绘制、触摸管理和输入响应。如果你的代码阻塞了主线程,那么程 序将出现反应迟钝。在执行I/O操作中,大多数情况下都会阻塞主线程,这些操作需要从外部资源读写,例如磁盘或者网络。

6. 让图片的大小跟UIImageView一样

如果需要将程序bundle中的图片显示到UIImageView中,请确保图 片和UIImageView的大小是一样的。因为图片的缩放非常耗费资 源,特别是将UIImageView嵌入到UIScro llView中。

如果是从远程服务中下载图片,有时候你控制不了图片的尺寸, 或者在下载之前无法在服务器上进行图片的缩放。这种情况,当 图片下载完之后,你可以手动进行图片的缩放,最好是在后台线程中,然后再在UIImageView中使用缩放过的图片。

7. 选择正确的集合

数组:是一个值按顺序排列的一个列表。根据索引可以快速查找,不过根据值进行查找就比较慢,另外插入和删除也比较慢。

字典: 存储键/值对。根据键可以快速查找。

Sets: 是一个值无序排列的列表,根据值可以快速查找,另外插入和删除也比较快。

8. 使用GZIP压缩

越来越多的程序依赖于外部数据,这些数据一般来自远程服务器或者其它的外部APIs 。使用GZIP对网络传输中的数据进行压缩,这样可以减小文件的大小,并加快下载的速度。压缩对于文本数据特别有用,因为文本具有很高的压缩比。

9. 重用和延迟加载View

程序界面中包含更多的view,意味着界面在显示的时候,需要进行更多的绘制任务,也就意味着需要消耗更多的CPU和内存资源。特别是在一个UIScro llView里面加入了许多view。 这种情况的管理技巧可以参考UITableView和UICollectionView的行为:不要一次性创建所有的subview,而是在需要的时候在创建view,并且当view使用完毕时候将它们添加到重用队列中。这样就可以仅在UIScrollView滚动的时候才配置view,以此可以避免分配创建view带来的成本。

现在有这样的一个问题:在程序中需要显示的view在什么时机创建(比如说,当用户点击某个按钮,需要显示某个view)。这里有两种可选方法:

1. 在屏幕第一次加载以及隐藏的时候,创建view;然后在需要的时候,再把view显示出来。

2. 直到需要显示view的时候,才创建并显示view。 每种方法都有各自的优点和确定。

使用第一种方法,需要消耗更多的内容,因为创建出来的view一直占据着内存,直到view被release掉。不过,使用这种方法,当用户点击按钮时,程序会很快的显示出view,因为只需要修改一下view的可见性即可。

而使用第二种方法则产生相反的效果,当需要的时候才创建view,这会消耗更少的内存,不过,当用户点击按钮的时候,不会立即显示出view。

10. 学会利用缓存

在开发程序时,一个重要的规则就是“缓存重要的内容”——这些内容一般不会改变,并且访问的频率比较高。远程服务器的响应内容,图片,甚至是计算结果,比如UITableView的行高都可用缓存

11. 考虑绘制

在iOS中制作漂亮的按钮有多种方法。可以使用全尺寸图片,可缩放图片,或者使用CALayer, CoreGraphics, 甚至是OpenGL来手 动测量和绘制按钮。

使用预渲染图片技术是最快的,因为iOS中不用等到在 屏幕上显示的时候才创建图形和对形状进行绘制(图片已经创建 好了!)。这样带来的问题是需要把所有的图片都放到程序bundle中,从而增加了程序的大小。

12. 处理内存警告

当系统内存偏低时,iOS会通知所有在运行的程序。

UIKit中提供了如下几种方法来接收低内存(low-memory)警告:

(1)实现app delegate中的applicationDidReceiveMemoryWarning: 方法。 

(2)在UIViewController子类中重写(Override)didReceiveMemoryWarning方法。 

(3)在通知中心里面注册UIApplicationDidReceiveMemoryWarningNotificatio通知。

在收到以上任意的警告时,需要立即释放任何不需要的内存。

例如,UIViewController的默认情况是清除掉当前不可见的view, 在UIViewController的子类中,可以清除一些额外的数据。程序中没有显示在当前屏幕中的图片也可以release掉。

13. 重用花销很大的对象

有些对象的初始化非常慢——比如NSDateFormatter和NSCalendar。不过有时候可以避免使用这些对象,例如在解析JSON/XML中的日期 时。当使用这些对象时,为了避免性能上的瓶颈,可以尝试尽量重用这些 对象——在类中添加一个属性或者创建一个静态变量。

注意,如果使用静态变量的话,对象会在程序运行的时候一直存在, 就像单例一样

下面的代码演示创建一个延迟加载的日期格式属性。第一次调用属性 的时候,会创建一个新的日期格式。之后再调用的话,会返回已经创 建好的实例对象:

// in your .h or inside a class extension

@property (nonatomic, strong) NSDateFormatter *formatter;

//insidetheimplementation(.m)

// When you need, just use self.formatter

 - (NSDateFormatter *)formatter {

if (! _formatter) {

_formatter = [[NSDateFormatter alloc] init];

_formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; // twitter date format

}

return _formatter; 

}

另外,还需要记住的是在设置NSDateFormatter的日期格式时,同样跟创建新的一个NSDateFormatter实例对象 时一样慢!因此, 在程序中如果需要频繁的处理日期格式,那么对NSDateFormatter进行重用是非常好的。

14. 避免重新处理数据

许多程序都需要从远程服务器中获取数据,以满足程序的需求。这些数据一般是JSON或XML格式。在请求和接收数据时,使用相同的数据结构非常重要。为什么呢?在内存中把数据转换为适合程序的数据格式是需要付出额外代价的。

例如,如果你需要在tableview中显示一些数据,那么请求和接收的数据格式最好是数组格式的,这样可以避免一 些中间操作——将数据转换为适合程序使用的数据结构。

类似的,如果程序是根据键来访问具体的值,那么最好请求和接收一个键/值对字典。

15. 选择正确的数据格式

将数据从程序传到网络服务器中有多种方法,其中使用的数据格式基本都是JSON和XML。你需要做的就是在程序中选择正确的数据格式。JSON的解析速度非常快,并且要比XML小得多,也就意味着只需要传输更少数据。

16. 设置适当的背景图片

有两种方法来给view设置一个背景图片:

1. 可以使用UIColor的colorWithPatternImge方法来创建一个颜色,并将这个颜色设置为view的背景颜色。

2. 可以给view添加一个UIImageView子视图。

如果你有一个全尺寸的背景图片,那么应该使用UIImageView,因为UIColor的colorWithPatternImge方法是用来创建小图片的——该图片会被重复使用。此时使用UIImageView会节省很多内存。

UIImageView*backgroundView=[[UIImageViewalloc]initWithImage:[UIImage imageNamed:@"background"]];

[self.view addSubview:backgroundView];

不过,如果你计划用小图片当做背景,那么应该使用UIColor的colorWithPatternImge方法。这种情况下绘制速度 会很快,并且不会消耗大量的内存。

self.view.backgroundColor=[UIColorcolorWithPatternImage:[UIImage imageNamed:@"background"]];

17. 降低Web内容的影响

UIWebView非常有用。用它可以很容易的显示web内容,甚至可以构建UIKit 空间难以显示的内容。不过,你可以能已经注意到程序中使用的UIWebView组建没有苹果的Saf ari程序快。这是因为JIT 编译限制了WebKit的Nitro引擎的使用。

因此为了获得更好的性能,需要调整一下HTML的大小。首先就是尽量的摆脱JavaScript,并避免使用大的框架, 例如jQuery。有时候使用原始的JavaScript要比别的框架快。

另外,尽量的异步加载JavaScript文件——特别是不直接影响到页面行为时,例如分析脚本。

最后——让使用到的图片,跟实际需要的一样大小。以此节省内存和提升速度。

18. 设置阴影路径

如果需要在view活layer中添加一个阴影,该如何处理呢? 大多数开发者首先将QuartzCore框架添加到工程中,然后添加如下代码:

1. #import <QuartzCore/QuartzCore.h> 2.

//Somewherelater...

UIView*view=[[UIViewalloc]init];

5.

// Setup the shadow ...

view.layer.shadowOffset = CGSizeMake(-1.0f, 1.0f);

view.layer.shadowRadius = 5.0f;

view.layer.shadowOpacity=0.6;

然而不幸的是上面这种方法有一个问题。Core Animation在渲染阴影效果之前,必须通过做一个离屏(of f screen)才能确定view的形状,而这个离屏操作非常耗费资源。

下面有一种方法可以更容易的让系统进行阴影渲染:设置阴影路径!

view.layer.shadowPath=[[UIBezierPathbezierPathWithRect:view.bounds]CGPath];

通过设置阴影路径,iOS就不用总是再计算该如何绘制阴影了。只需要使用你预先计算好的路径即可。有一点不好的是,根据view的格式,自己可能很难计算出路径。另外一个问题就是当view的frame改变时,必须每次都更新一下阴影路径。

19. 优化tableView

tableview需要快速的滚动——如果不能的话,用户会感觉到停顿。

为了让table view平滑的滚动,确保遵循了如下建议:

(1)设置正确的reuseIdentifer以重用cell。

(2) 尽量将view设置为不透明,包括cell本身。 

(3)避免渐变,图像缩放以及离屏绘制。

(4)如果row的高度不相同,那么将其缓存下来。

(5) 如果cell显示的内容来自网络,那么确保这些内容是通过异步来获取的。 

(6)使用shadowPath来设置阴影。

(7)减少subview的数量。 

(8)在cellForRowAtIndexPath:中尽量做更少的操作。如果需要做一些处理,那么最好做过一次之后,就将结果缓存起来。

(9)使用适当的数据结构来保存需要的信息。不同的结构会带来不同的操作代价。

(10)使用rowHeight, sectionFooterHeight 和 sectionHeaderHeight 来设置一个恒定高度,  而不要从delegate中获取。

20. 选择正确的数据存储方式

当需要存储和读取大量的数据时,该如何选择存储方式呢? 有如下选择:

 (1)使用NSUserDefaults进行存储保存为XML

(2)JSON或Plist 格式的文件 

(3)利用NSCoding进行归档存储到一个本地数据库,例如SQLit e。 

(4)使用Core Data

虽然NSUserDef aults很好并且容易,不过只只针对于存储小量数据

大量数据保存为结构化的文件也可能会带来问题。一般,在解析这些结构数据之前,需要将内容全部加载到内存 中,这是很消耗资源的。虽然可以使用SAX来处理XML文件,但是这有点复杂。另外,加载到内存中的所有对象, 不一定全部都需要用到。

那么使用NSCoding来保存大量数据怎么样呢? 因为它同样是对文件进行读写,因此依然存在上面说的问题。

要保存大量的数据,最好使用SQLite或Core Data。通过SQLite或Core Data可以进行具体的查询——只需要获取

并加载需要的数据对象——避免对数据进行不合理的搜索。在性能方面,SQLite和Core Data差不大。

SQLite和Core Data最大的区别实际上就是用法上。Core Data代表一个对象模型,而SQLite只是一个DBMS。

一般,苹果建议使用Core Data,不过如果你有特殊的原因不能使用Core Data的话,可以使用低级别的SQLite。

在程序中,如果选择使用SQLite,这里有个方便的库FMDB :可以利用该库操作SQLite数据库,而不用深入使用SQLite C API。

21. 加速启动时间

(1)让程序尽量快速启动的方法就是尽量以异步方式执行任务,例如网络请求,数据访问或解析。

(2)另外,避免使用臃肿的XIBs,因为XIB的加载是在主线程中进行的。

22. 尽量避免Date格式化

如果有许多日期需要使用NSDateFormatter,那么需要小心对待了。如之前(重用花销很大的对象)所提到的,

无论什么时候,都应该尽量重用NSDateFormatters。

然而,如果你需要更快的速度,那么应该使用C来直接解析日期,而不NSDateFormatter。Sam Sof fes写了一 篇文章,其中提供了一些解析ISO- 8601格式日期字符的串代码。你只需要简单的调整一下其中的代码就可以满足自己特殊的需求了。

如果你自己能控制处理日期的格式,那么可以选择 Unix timestamps。Unix timestamps是一个简单的整数,代表 了从新纪元时间(epoch)开始到现在已经过了多少秒,通常这个新纪元参考时间是00:00:00 UTC on 1 January 1970。

你可以很容易的见这个时间戳转换为NSDat e,如下所示:

- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp { 

          ret urn [NSDat e dat eWit hTimeInt ervalSince1970:t imest amp];

 }

上面这个方法比C函数还要快! 注意:许多网络APIs返回的时间戳都是毫秒,因此需要注意的是在将这个时间戳传递给dateFromUnixTimestamp之前需要除以1000。

关于iOS 启动性能优化可参考: https://mp.weixin.qq.com/s/Kf3EbDIUuf0aWVT-UCEmbA

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

推荐阅读更多精彩内容