iOS应用性能调优的25个建议和技巧.docx
- 文档编号:5947944
- 上传时间:2023-01-02
- 格式:DOCX
- 页数:19
- 大小:230.71KB
iOS应用性能调优的25个建议和技巧.docx
《iOS应用性能调优的25个建议和技巧.docx》由会员分享,可在线阅读,更多相关《iOS应用性能调优的25个建议和技巧.docx(19页珍藏版)》请在冰豆网上搜索。
iOS应用性能调优的25个建议和技巧
iOS应用性能调优的25个建议和技巧
本文来自iOSTutorialTeam的MarceloFabri,他是Movile的一名iOS程序员。
这是他的个人网站:
性能对iOS应用的开发尤其重要,如果你的应用失去反应或者很慢,失望的用户会把他们的失望写满AppStore的评论。
然而由于iOS设备的限制,有时搞好性能是一件难事。
开发过程中你会有很多需要注意的事项,你也很容易在做出选择时忘记考虑性能影响。
这正是我写下这篇文章的原因。
这篇文章以一个方便查看的核对表的形式整合了你可以用来提升你app性能的25条建议和技巧。
请耐心读完这篇文章,为你未来的app提个速!
注意:
每在优化代码之前,你都要注意一个问题,不要养成”预优化”代码的错误习惯。
时常使用Instruments去profile你的代码来发现需要提升的方面。
MattGalloway写过一篇很棒的如何利用Instruments来优化代码的文章。
还要注意的是,这里列出的其中一些建议是有代价的,所建议的方式会提升app的速度或者使它更加高效,但也可能需要花很多功夫去应用或者使代码变得更加复杂,所以要仔细选择。
目录
我要给出的建议将分为三个不同的等级:
入门级、中级和进阶级:
入门级(这是些你一定会经常用在你app开发中的建议)
∙1.用ARC管理内存
∙2.在正确的地方使用reuseIdentifier
∙3.尽可能使Views透明
∙4.避免庞大的XIB
∙5.不要block主线程
∙6.在ImageViews中调整图片大小
∙7.选择正确的Collection
∙8.打开gzip压缩
中级(这些是你可能在一些相对复杂情况下可能用到的)
∙9.重用和延迟加载Views
∙10.Cache,Cache,还是Cache!
∙11.权衡渲染方法
∙12.处理内存警告
∙13.重用大开销的对象
∙14.使用SpriteSheets
∙15.避免反复处理数据
∙16.选择正确的数据格式
∙17.正确地设定BackgroundImages
∙18.减少使用Web特性
∙19.设定ShadowPath
∙20.优化你的TableView
∙21.选择正确的数据存储选项
进阶级(这些建议只应该在你确信他们可以解决问题和得心应手的情况下采用)
∙22.加速启动时间
∙23.使用AutoreleasePool
∙24.选择是否缓存图片
∙25.尽量避免日期格式转换
无需赘述,让我们进入正题吧~
初学者性能提升
这个部分致力于一些能提高性能的基本改变。
但所有层次的开发者都有可能会从这个记录了一些被忽视的项目的小小的性能备忘录里获得一些提升。
1.用ARC管理内存
ARC(AutomaticReferenceCounting,自动引用计数)和iOS5一起发布,它避免了最常见的也就是经常是由于我们忘记释放内存所造成的内存泄露。
它自动为你管理retain和release的过程,所以你就不必去手动干预了。
下面是你会经常用来去创建一个View的代码段:
1
2
3
4
UIView*view=[[UIViewalloc]init];
//...
[self.viewaddSubview:
view];
[viewrelease];
忘掉代码段结尾的release简直像记得吃饭一样简单。
而ARC会自动在底层为你做这些工作。
除了帮你避免内存泄露,ARC还可以帮你提高性能,它能保证释放掉不再需要的对象的内存。
这都啥年代了,你应该在你的所有项目里使用ARC!
这里有一些更多关于ARC的学习资源:
∙Apple’sofficialdocumentation
∙MatthijsHollemans’sBeginningARCiniOSTutorial
∙TonyDahbura’sHowToEnableARCinaCocos2D2.XProject
∙Ifyoustillaren’tconvincedofthebenefitsofARC,checkoutthisarticleoneightmythsaboutARCtoreallyconvinceyouwhyyoushouldbeusingit!
ARC当然不能为你排除所有内存泄露的可能性。
由于阻塞,retain周期,管理不完善的CoreFoundationobject(还有C结构)或者就是代码太烂依然能导致内存泄露。
这里有一篇很棒的介绍ARC不能做到以及我们该怎么做的文章
2.在正确的地方使用reuseIdentifier
一个开发中常见的错误就是没有给UITableViewCells,UICollectionViewCells,甚至是UITableViewHeaderFooterViews设置正确的reuseIdentifier。
为了性能最优化,tableview用`tableView:
cellForRowAtIndexPath:
`为rows分配cells的时候,它的数据应该重用自UITableViewCell。
一个tableview维持一个队列的数据可重用的UITableViewCell对象。
不使用reuseIdentifier的话,每显示一行tableview就不得不设置全新的cell。
这对性能的影响可是相当大的,尤其会使app的滚动体验大打折扣。
自iOS6起,除了UICollectionView的cells和补充views,你也应该在header和footerviews中使用reuseIdentifiers。
想要使用reuseIdentifiers的话,在一个tableview中添加一个新的cell时在datasourceobject中添加这个方法:
1
2
staticNSString*CellIdentifier=@"Cell";
UITableViewCell*cell=[tableViewdequeueReusableCellWithIdentifier:
CellIdentifierforIndexPath:
indexPath];
这个方法把那些已经存在的cell从队列中排除,或者在必要时使用先前注册的nib或者class创造新的cell。
如果没有可重用的cell,你也没有注册一个class或者nib的话,这个方法返回nil。
3.尽量把views设置为透明
如果你有透明的Views你应该设置它们的opaque属性为YES。
原因是这会使系统用一个最优的方式渲染这些views。
这个简单的属性在IB或者代码里都可以设定。
Apple的文档对于为图片设置透明属性的描述是:
(opaque)这个属性给渲染系统提供了一个如何处理这个view的提示。
如果设为YES,渲染系统就认为这个view是完全不透明的,这使得渲染系统优化一些渲染过程和提高性能。
如果设置为NO,渲染系统正常地和其它内容组成这个View。
默认值是YES。
在相对比较静止的画面中,设置这个属性不会有太大影响。
然而当这个view嵌在scrollview里边,或者是一个复杂动画的一部分,不设置这个属性的话会在很大程度上影响app的性能。
你可以在模拟器中用Debug\ColorBlendedLayers选项来发现哪些view没有被设置为opaque。
目标就是,能设为opaque的就全设为opaque!
4.避免过于庞大的XIB
iOS5中加入的Storyboards(分镜)正在快速取代XIB。
然而XIB在一些场景中仍然很有用。
比如你的app需要适应iOS5之前的设备,或者你有一个自定义的可重用的view,你就不可避免地要用到他们。
如果你不得不XIB的话,使他们尽量简单。
尝试为每个Controller配置一个单独的XIB,尽可能把一个ViewController的view层次结构分散到单独的XIB中去。
需要注意的是,当你加载一个XIB的时候所有内容都被放在了内存里,包括任何图片。
如果有一个不会即刻用到的view,你这就是在浪费宝贵的内存资源了。
Storyboards就是另一码事儿了,storyboard仅在需要时实例化一个viewcontroller.
当家在XIB是,所有图片都被chache,如果你在做OSX开发的话,声音文件也是。
Apple在相关文档中的记述是:
当你加载一个引用了图片或者声音资源的nib时,nib加载代码会把图片和声音文件写进内存。
在OSX中,图片和声音资源被缓存在namedcache中以便将来用到时获取。
在iOS中,仅图片资源会被存进namedcaches。
取决于你所在的平台,使用NSImage或UIImage的`imageNamed:
`方法来获取图片资源。
很明显,同样的事情也发生在storyboards中,但我并没有找到任何支持这个结论的文档。
如果你了解这个操作,写信给我!
想要了解更多关于storyboards的内容的话你可以看看MatthijsHollemans的BeginningStoryboardsiniOS5Part1和Part2
5.不要阻塞主线程
永远不要使主线程承担过多。
因为UIKit在主线程上做所有工作,渲染,管理触摸反应,回应输入等都需要在它上面完成。
一直使用主线程的风险就是如果你的代码真的block了主线程,你的app会失去反应。
这。
。
。
正是在AppStore中拿到一颗星的捷径:
]
大部分阻碍主进程的情形是你的app在做一些牵涉到读写外部资源的I/O操作,比如存储或者网络。
你可以使用`NSURLConnection`异步地做网络操作:
+(void)sendAsynchronousRequest:
(NSURLRequest*)requestqueue:
(NSOperationQueue*)queuecompletionHandler:
(void(^)(NSURLResponse*,NSData*,NSError*))handler
或者使用像AFNetworking这样的框架来异步地做这些操作。
如果你需要做其它类型的需要耗费巨大资源的操作(比如时间敏感的计算或者存储读写)那就用GrandCentralDispatch,或者NSOperation和NSOperationQueues.
下面代码是使用GCD的模板
1
2
3
4
5
6
7
8
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
//switchtoabackgroundthreadandperformyourexpensiveoperation
dispatch_async(dispatch_get_main_queue(),^{
//switchbacktothemainthreadtoupdateyourUI
});
});
发现代码中有一个嵌套的`dispatch_async`吗?
这是因为任何UIKit相关的代码需要在主线程上进行。
如果你对NSOperation或者GCD的细节感兴趣的话,看看RayWenderlich的MultithreadingandGrandCentralDispatchoniOSforBeginners,还有SoheilAzarpour的HowToUseNSOperationsandNSOperationQueues教程。
6.在ImageViews中调整图片大小
如果要在`UIImageView`中显示一个来自bundle的图片,你应保证图片的大小和UIImageView的大小相同。
在运行中缩放图片是很耗费资源的,特别是`UIImageView`嵌套在`UIScrollView`中的情况下。
如果图片是从远端服务加载的你不能控制图片大小,比如在下载前调整到合适大小的话,你可以在下载完成后,最好是用backgroundthread,缩放一次,然后在UIImageView中使用缩放后的图片。
7.选择正确的Collection
学会选择对业务场景最合适的类或者对象是写出能效高的代码的基础。
当处理collections时这句话尤其正确。
Apple有一个CollectionsProgrammingTopics的文档详尽介绍了可用的classes间的差别和你该在哪些场景中使用它们。
这对于任何使用collections的人来说是一个必读的文档。
呵呵,我就知道你因为太长没看…这是一些常见collection的总结:
∙Arrays:
有序的一组值。
使用index来lookup很快,使用valuelookup很慢,插入/删除很慢。
∙Dictionaries:
存储键值对。
用键来查找比较快。
∙Sets:
无序的一组值。
用值来查找很快,插入/删除很快。
8.打开gzip压缩
大量app依赖于远端资源和第三方API,你可能会开发一个需要从远端下载XML,JSON,HTML或者其它格式的app。
问题是我们的目标是移动设备,因此你就不能指望网络状况有多好。
一个用户现在还在edge网络,下一分钟可能就切换到了3G。
不论什么场景,你肯定不想让你的用户等太长时间。
减小文档的一个方式就是在服务端和你的app中打开gzip。
这对于文字这种能有更高压缩率的数据来说会有更显著的效用。
好消息是,iOS已经在NSURLConnection中默认支持了gzip压缩,当然AFNetworking这些基于它的框架亦然。
像GoogleAppEngine这些云服务提供者也已经支持了压缩输出。
如果你不知道如何利用Apache或者IIS(服务器)来打开gzip,可以读下这篇文章。
中级性能提升
你确信你已经掌握了前述那些基础级的优化方案了吗?
但实际情况是,有时一些解决方案并不像那些一样明显,它们往往严重依赖于你如何架构和书写你的app。
下面的这些建议就是针对这些场景的。
9.重用和延迟加载(lazyload)Views
更多的view意味着更多的渲染,也就是更多的CPU和内存消耗,对于那种嵌套了很多view在UIScrollView里边的app更是如此。
这里我们用到的技巧就是模仿`UITableView`和`UICollectionView`的操作:
不要一次创建所有的subview,而是当需要时才创建,当它们完成了使命,把他们放进一个可重用的队列中。
这样的话你就只需要在滚动发生时创建你的views,避免了不划算的内存分配。
创建views的能效问题也适用于你app的其它方面。
想象一下一个用户点击一个按钮的时候需要呈现一个view的场景。
有两种实现方法:
∙1.创建并隐藏这个view当这个screen加载的时候,当需要时显示它;
∙2.当需要时才创建并展示。
每个方案都有其优缺点。
用第一种方案的话因为你需要一开始就创建一个view并保持它直到不再使用,这就会更加消耗内存。
然而这也会使你的app操作更敏感因为当用户点击按钮的时候它只需要改变一下这个view的可见性。
第二种方案则相反-消耗更少内存,但是会在点击按钮的时候比第一种稍显卡顿。
10.Cache,Cache,还是Cache!
一个极好的原则就是,缓存所需要的,也就是那些不大可能改变但是需要经常读取的东西。
我们能缓存些什么呢?
一些选项是,远端服务器的响应,图片,甚至计算结果,比如UITableView的行高。
NSURLConnection默认会缓存资源在内存或者存储中根据它所加载的HTTPHeaders。
你甚至可以手动创建一个NSURLRequest然后使它只加载缓存的值。
下面是一个可用的代码段,你可以可以用它去为一个基本不会改变的图片创建一个NSURLRequest并缓存它:
1
2
3
4
5
6
7
8
9
10
+(NSMutableURLRequest*)imageRequestWithURL:
(NSURL*)url{
NSMutableURLRequest*request=[NSMutableURLRequestrequestWithURL:
url];
request.cachePolicy=NSURLRequestReturnCacheDataElseLoad;//thiswillmakesuretherequestalwaysreturnsthecachedimage
request.HTTPShouldHandleCookies=NO;
request.HTTPShouldUsePipelining=YES;
[requestaddValue:
@"image/*"forHTTPHeaderField:
@"Accept"];
returnrequest;
}
注意你可以通过NSURLConnection获取一个URLrequest,AFNetworking也一样的。
这样你就不必为采用这条tip而改变所有的networking代码了。
如果想了解更多关于HTTPcaching,NSURLCache,NSURLConnection的相关知识,可以读下这篇文章()
如果你需要缓存其它不是HTTPRequest的东西,你可以用NSCache。
NSCache和NSDictionary类似,不同的是系统回收内存的时候它会自动删掉它的内容。
MatttThompson有一篇很棒的关于它的文章:
:
如果你对HTTP感兴趣可以读下Google的这篇best-practicesdocumentonHTTPcaching。
11.权衡渲染方法
在iOS中可以有很多方法做出漂亮的按钮。
你可以用整幅的图片,可调大小的图片,uozhe可以用CALayer,CoreGraphics甚至OpenGL来画它们。
当然每个不同的解决方法都有不同的复杂程度和相应的性能。
有一篇AppleUIKitteam中的一员AndyMatuschak推荐过的很棒的关于graphic性能的帖子很值得一读。
简单来说,就是用事先渲染好的图片更快一些,因为如此一来iOS就免去了创建一个图片再画东西上去然后显示在屏幕上的程序。
问题是你需要把所有你需要用到的图片放到app的bundle里面,这样就增加了体积–这就是使用可变大小的图片更好的地方了:
你可以省去一些不必要的空间,也不需要再为不同的元素(比如按钮)来做不同的图。
然而,使用图片也意味着你失去了使用代码调整图片的机动性,你需要一遍又一遍不断地重做他们,这样就很浪费时间了,而且你如果要做一个动画效果,虽然每幅图只是一些细节的变化你就需要很多的图片造成bundle大小的不断增大。
总得来说,你需要权衡一下利弊,到底是要性能能还是要bundle保持合适的大小。
12.处理内存警告
一旦系统内存过低,iOS会通知所有运行中app。
在官方文档中是这样记述:
如果你的app收到了内存警告,它就需要尽可能释放更多的内存。
最佳方式是移除对缓存,图片object和其他一些可以重创建的objects的strongreferences.
幸运的是,UIKit提供了几种收集低内存警告的方法:
∙在appdelegate中使用`applicationDidReceiveMemoryWarning:
`的方法
∙在你的自定义UIViewController的子类(subclass)中覆盖`didReceiveMemoryWarning`
∙注册并接收UIApplicationDidReceiveMemoryWarningNotification的通知
一旦收到这类通知,你就需要释放任何不必要的内存使用。
例如,UIViewController的默认行为是移除一些不可见的view,它的一些子类则可以补充这个方法,删掉一些额外的数据结构。
一个有图片缓存的app可以移除不在屏幕上显示的图片。
这样对内存警报的处理是很必要的,若不重视,你的app就可能被系统杀掉。
然而,当你一定要确认你所选择的object是可以被重现创建的来释放内存。
一定要在开发中用模拟器中的内存提醒模拟去测试一下。
13.重用大开销对象
一些objects的初始化很慢,比如NSDateFormatter和NSCalendar。
然而,你又不可避免地需要使用它们,比如从JSON或者XML中解析数据。
想要避免使用这个对象的瓶颈你就需要重用他们,可以通过添加属性到你的class里或者创建静态变量来实现。
注意如果你要选择第二种方法,对象会在你的app运行时一直存在于内存中,和单例(singleton)很相似。
下面的代码说明了使用一个属性来延迟加载一个dateformatter.第一次调用时它会创建一个新的实例,以后的调用则将返回已经创建的实例:
1
2
3
4
5
6
7
8
9
10
11
12
//inyour.horinsideaclassextension
@property(nonatomic,strong)NSDateFormatter*formatter;
//insidetheimplementation(.m)
//Whenyouneed,justuseself.formatter
-(NSDateFormatter*)formatter{
if(!
_formatter){
_formatter=[[NSDateFormatteralloc]init];
_formatter.dateFormat=@"EEEMMMddHH:
mm:
ssZyyyy";//twitterdateformat
}
return_formatter;
}
还需要注意的是,其实设置一个NSDateFormatter的速度差不多是和创建新的一样慢的!
所以如果你的app需要经常进行日期格式处理的话,你会从这个方法中得到不小的性能提升。
14.使用SpriteSheets
你是一个游戏开发者吗,那么Spritesheets一定是一个你的最好的朋友了。
Spritesheet可以让渲染速度加快,甚至比标准的屏幕渲染方法节省内存。
我们有两个很好的关于Sprite的教程:
1HowToUseAnimation
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- iOS 应用 性能 25 建议 技巧