iOS应用性能调优的25个建议和技巧
本文来自iOSTutorial Team 的 Marcelo Fabri,他是Movile的一名 iOS 程序猿。这是他的个人站点:http://www.marcelofabri.com/,你还能够在Twitter上关注@marcelofabri_。
性能对 iOS 应用的开发尤其重要,假设你的应用失去反应或者非常慢,失望的用户会把他们的失望写满AppStore的评论。然而因为iOS设备的限制,有时搞好性能是一件难事。开发过程中你会有非常多须要注意的事项。你也非常easy在做出选择时忘记考虑性能影响。
这正是我写下这篇文章的原因。
这篇文章以一个方便查看的核对表的形式整合了你能够用来提升你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(AutomaticReference Counting, 自己主动引用计数)和iOS5一起公布,它避免了最常见的也就是常常是因为我们忘记释放内存所造成的内存泄露。它自己主动为你管理retain和release的过程,所以你就不必去手动干预了。
以下是你会经经常使用来去创建一个View的代码段:
1 2 3 4 |
UIView *view = [[UIView alloc] init]; // ... [self.view addSubview:view]; [view release];
|
忘掉代码段结尾的release简直像记得吃饭一样简单。而ARC会自己主动在底层为你做这些工作。
除了帮你避免内存泄露,ARC还能够帮你提高性能,它能保证释放掉不再须要的对象的内存。
这都啥年代了。你应该在你的全部项目里使用ARC!
这里有一些很多其它关于ARC的学习资源:
· Apple’sofficial documentation
· Matthijs Hollemans’s Beginning ARC in iOS Tutorial
· Tony Dahbura’s How To Enable ARC in a Cocos2D 2.X Project
· If you still aren’t convinced of the benefits of ARC, check outthis article on eight myths about ARC to really convince you whyyou should be using it!
ARC当然不能为你排除全部内存泄露的可能性。
因为堵塞,retain 周期, 管理不完好的CoreFoundationobject(还有C结构)或者就是代码太烂依旧能导致内存泄露。
这里有一篇非常棒的介绍ARC不能做到以及我们该怎么做的文章http://conradstoll.com/blog/2013/1/19/blocks-operations-and-retain-cycles.html。
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时在datasource object中加入这种方法:
1 2 |
staticNSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath: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的性能。
你能够在模拟器中用DebugColorBlended Layers选项来发现哪些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,假设你在做OS X开发的话。声音文件也是。Apple在相关文档中的记述是:
当你载入一个引用了图片或者声音资源的nib时,nib载入代码会把图片和声音文件写进内存。
在OS X中,图片和声音资源被缓存在namedcache中以便将来用到时获取。在iOS中。仅图片资源会被存进namedcaches。取决于你所在的平台。使用NSImage 或UIImage的`imageNamed:`方法来获取图片资源。
非常明显,相同的事情也发生在storyboards中,但我并没有找到不论什么支持这个结论的文档。假设你了解这个操作,写信给我!
想要了解很多其它关于storyboards的内容的话你能够看看Matthijs Hollemans的Beginning Storyboards in iOS 5 Part 1和Part 2
5. 不要堵塞主线程
永远不要使主线程承担过多。由于UIKit在主线程上做全部工作,渲染,管理触摸反应,回应输入等都须要在它上面完毕。
一直使用主线程的风险就是假设你的代码真的block了主线程,你的app会失去反应。这。
。。正是在AppStore中拿到一颗星的捷径 :]
大部分阻碍主进程的情形是你的app在做一些牵涉到读写外部资源的I/O操作,比方存储或者网络。
你能够使用`NSURLConnection`异步地做网络操作:
+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue*)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler
或者使用像AFNetworking这种框架来异步地做这些操作。
假设你须要做其他类型的须要耗费巨大资源的操作(比方时间敏感的计算或者存储读写)那就用 GrandCentral Dispatch。或者 NSOperation 和NSOperationQueues.
以下代码是使用GCD的模板
1 2 3 4 5 6 7 8 |
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // switch to a background thread and perform your expensive operation
dispatch_async(dispatch_get_main_queue(), ^{ // switch back to the main thread to update your UI
}); });
|
发现代码中有一个嵌套的`dispatch_async`吗?这是由于不论什么UIKit相关的代码须要在主线程上进行。
假设你对NSOperation 或者GCD 的细节感兴趣的话,看看RayWenderlich的Multithreading and Grand Central Dispatch on iOS forBeginners。还有 SoheilAzarpour 的How To Use NSOperations and NSOperationQueues教程。
6. 在Image Views中调整图片大小
假设要在`UIImageView`中显示一个来自bundle的图片,你应保证图片的大小和UIImageView的大小同样。在执行中缩放图片是非常耗费资源的,特别是`UIImageView`嵌套在`UIScrollView`中的情况下。
假设图片是从远端服务载入的你不能控制图片大小。比方在下载前调整到合适大小的话。你能够在下载完毕后,最好是用backgroundthread,缩放一次。然后在UIImageView中使用缩放后的图片。
7. 选择正确的Collection
学会选择对业务场景最合适的类或者对象是写出能效高的代码的基础。当处理collections时这句话尤其正确。
Apple有一个Collections Programming Topics的文档详尽介绍了可用的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这些基于它的框架亦然。
像GoogleApp Engine这些云服务提供者也已经支持了压缩输出。
假设你不知道怎样利用Apache或者IIS(server)来打开gzip,能够读下这篇文章。
中级性能提升
你确信你已经掌握了前述那些基础级的优化方案了吗?但实际情况是,有时一些解决方式并不像那些一样明显,它们往往严重依赖于你怎样架构和书写你的app。以下的这些建议就是针对这些场景的。
9. 重用和延迟载入(lazy load) Views
很多其它的view意味着很多其它的渲染,也就是很多其它的CPU和内存消耗,对于那种嵌套了非常多view在UIScrollView里边的app更是如此。
这里我们用到的技巧就是模仿`UITableView`和`UICollectionView`的操作: 不要一次创建全部的subview。而是当须要时才创建,当它们完毕了使命。把他们放进一个可重用的队列中。
这种话你就仅仅须要在滚动发生时创建你的views,避免了不划算的内存分配。
创建views的能效问题也适用于你app的其他方面。
想象一下一个用户点击一个button的时候须要呈现一个view的场景。有两种实现方法:
· 1. 创建并隐藏这个view当这个screen载入的时候。当须要时显示它;
· 2. 当须要时才创建并展示。
每一个方案都有其优缺点。
用第一种方案的话由于你须要一開始就创建一个view并保持它直到不再使用。这就会更加消耗内存。
然而这也会使你的app操作更敏感由于当用户点击button的时候它仅仅须要改变一下这个view的可见性。
另外一种方案则相反-消耗更少内存,可是会在点击button的时候比第一种稍显卡顿。
10. Cache, Cache, 还是Cache!
一个极好的原则就是,缓存所须要的。也就是那些不大可能改变可是须要常常读取的东西。
我们能缓存些什么呢?一些选项是。远端server的响应,图片,甚至计算结果。比方UITableView的行高。
NSURLConnection默认会缓存资源在内存或者存储中依据它所载入的HTTPHeaders。你甚至能够手动创建一个NSURLRequest然后使它仅仅载入缓存的值。
以下是一个可用的代码段,你能够能够用它去为一个基本不会改变的图片创建一个NSURLRequest并缓存它:
1 2 3 4 5 6 7 8 9 10 |
+ (NSMutableURLRequest *)imageRequestWithURL:(NSURL *)url { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;// this will make sure the request always returns the cached image request.HTTPShouldHandleCookies = NO; request.HTTPShouldUsePipelining = YES; [request addValue:@"image/*"forHTTPHeaderField:@"Accept"];
returnrequest; }
|
注意你能够通过NSURLConnection 获取一个URL request。AFNetworking也一样的。
这样你就不必为採用这条tip而改变全部的networking代码了。
假设想了解很多其它关于HTTPcaching, NSURLCache, NSURLConnection的相关知识。能够读下这篇文章()
假设你须要缓存其他不是HTTPRequest的东西。你能够用NSCache。
NSCache和NSDictionary类似,不同的是系统回收内存的时候它会自己主动删掉它的内容。
MatttThompson有一篇非常棒的关于它的文章::http://nshipster.com/nscache/
假设你对HTTP感兴趣能够读下Google的这篇best-practices document on HTTP caching。
11. 权衡渲染方法
在iOS中能够有非常多方法做出美丽的button。
你能够用整幅的图片。可调大小的图片。uozhe能够用CALayer,CoreGraphics甚至OpenGL来画它们。
当然每一个不同的解决方法都有不同的复杂程度和对应的性能。
有一篇AppleUIKit team中的一员Andy Matuschak推荐过的非常棒的关于graphic性能的帖子非常值得一读。
简单来说,就是用事先渲染好的图片更快一些。由于如此一来iOS就免去了创建一个图片再画东西上去然后显示在屏幕上的程序。问题是你须要把全部你须要用到的图片放到app的bundle里面。这样就添加了体积–这就是使用可变大小的图片更好的地方了: 你能够省去一些不必要的空间。也不须要再为不同的元素(比方button)来做不同的图。
然而,使用图片也意味着你失去了使用代码调整图片的机动性。你须要一遍又一遍不断地重做他们,这样就非常浪费时间了。并且你假设要做一个动画效果,尽管每幅图仅仅是一些细节的变化你就须要非常多的图片造成bundle大小的不断增大。
总得来说,你须要权衡一下利弊。究竟是要性能能还是要bundle保持合适的大小。
12. 处理内存警告
一旦系统内存过低,iOS会通知全部执行中app。在官方文档中是这样记述:
假设你的app收到了内存警告。它就须要尽可能释放很多其它的内存。最佳方式是移除对缓存。图片object和其它一些能够重创建的objects的strongreferences.
幸运的是。UIKit提供了几种收集低内存警告的方法:
· 在app delegate中使用`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 |
// in your .h or inside a class extension @property (nonatomic, strong) NSDateFormatter *formatter;
// inside the implementation (.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的速度差点儿相同是和创建新的一样慢的!
所以假设你的app须要常常进行日期格式处理的话。你会从这种方法中得到不小的性能提升。
14. 使用Sprite Sheets
你是一个游戏开发人员吗,那么Spritesheets一定是一个你的最好的朋友了。Sprite sheet能够让渲染速度加快。甚至比标准的屏幕渲染方法节省内存。
我们有两个非常好的关于Sprite的教程:
1 How To Use Animations and Sprite Sheets in Cocos2D
2 How to Create and Optimize Sprite Sheets in Cocos2D withTexture Packer and Pixel Formats
第二个教程涵盖了可能在非常大程度上影响你游戏性能的pixel格式的细节。
假设你对于spirtesheet还不是非常熟悉,能够看下这两个(youtube)视频SpriteSheets– TheMovie, Part 1和Part2。视频的作者是创建Sprite sheet非常流行的工具之中的一个TexturePacker的作者Andreas Löw。
除了使用Spritesheets,其他写在这里的建议当然也能够用于游戏开发中。
比方你须要非常多的Spritesheets,像敌人,导弹之类的动作类必备元素。你能够重用这些sprites而不用每次都要又一次创建。
15. 避免重复处理数据
很多应用须要从server载入功能所需的常为JSON或者XML格式的数据。在server端和client使用同样的数据结构非常重要。在内存中操作数据使它们满足你的数据结构是开销非常大的。
比方你须要数据来展示一个tableview,最好直接从server取array结构的数据以避免额外的中间数据结构改变。
类似的,假设须要从特定key中取数据,那么就使用键值对的dictionary。
16. 选择正确的数据格式
从app和网络服务间数据传输有非常多方案,最常见的就是JSON和XML。
你须要选择对你的app来说最合适的一个。
解析JSON会比XML更快一些,JSON也通常更小更便于传输。
从iOS5起有了官方内建的JSON deserialization就更加方便使用了。
可是XML也有XML的优点,比方使用SAX来解析XML就像解析本地文件一样,你不需像解析json一样等到整个文档下载完毕才開始解析。当你处理非常大的数据的时候就会极大地减低内存消耗和添加性能。
17. 正确设定背景图片
在View里放背景图片就像非常多其他iOS编程一样有非常多方法:
3 使用UIColor的colorWithPatternImage来设置背景色。
4 在view中加入一个UIImageView作为一个子View。
假设你使用全画幅的背景图。你就必须使用UIImageView由于UIColor的colorWithPatternImage是用来创建小的反复的图片作为背景的。
这样的情形下使用UIImageView能够节约不少的内存:
1 2 3 |
// You could also achieve the same result in Interface Builder UIImageView *backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background"]]; [self.view addSubview:backgroundView];
|
假设你用小图平铺来创建背景。你就须要用UIColor的colorWithPatternImage来做了,它会更快地渲染也不会花费非常多内存:
1 |
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background"]];
|
18. 降低使用Web特性
UIWebView非常实用,用它来展示网页内容或者创建UIKit非常难做到的动画效果是非常easy的一件事。
可是你可能有注意到UIWebView并不像驱动Safari的那么快。这是因为以JIT compilation为特色的Webkit的NitroEngine的限制。
所以想要更高的性能你就要调整下你的HTML了。第一件要做的事就是尽可能移除不必要的javascript。避免使用过大的框架。
能仅仅用原生js就更好了。
另外。尽可能异步载入比如用户行为统计script这样的不影响页面表达的javascript。
最后。永远要注意你使用的图片。保证图片的符合你使用的大小。使用Spritesheet提高载入速度和节约内存。
很多其它相关信息能够看下WWDC2012 session #601 – Optimizing Web Content inUIWebViews and Websites on iOS
19. 设定Shadow Path
怎样在一个View或者一个layer上加一个shadow呢,QuartzCore框架是非常多开发人员的选择:
1 2 3 4 5 6 7 8 9 |
#import <QuartzCore/QuartzCore.h>
// Somewhere later ... UIView *view = [[UIView alloc] init];
// Setup the shadow ... view.layer.shadowOffset = CGSizeMake(-1.0f, 1.0f); view.layer.shadowRadius = 5.0f; view.layer.shadowOpacity = 0.6;
|
看起来非常easy,对吧。
但是,坏消息是使用这种方法也有它的问题… Core Animation不得不先在后台得出你的图形并加好阴影然后才渲染。这开销是非常大的。
使用shadowPath的话就避免了这个问题:
view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath];
使用shadowpath的话iOS就不必每次都计算怎样渲染。它使用一个预先计算好的路径。
但问题是自己计算path的话可能在某些View中比較困难,且每当view的frame变化的时候你都须要去updateshadow path.
想了解很多其它能够看看MarkPospesel的这篇。
20. 优化Table View
Tableview须要有非常好的滚动性能,不然用户会在滚动过程中发现动画的瑕疵。
为了保证tableview平滑滚动,确保你採取了下面的措施:
· 正确使用`reuseIdentifier`来重用cells
· 尽量使全部的view opaque,包含cell自身
· 避免渐变。图片缩放,后台选人
· 缓存行高
· 假设cell内现实的内容来自web,使用异步载入,缓存请求结果
· 使用`shadowPath`来画阴影
· 降低subviews的数量
· 尽量不适用`cellForRowAtIndexPath:`。假设你须要用到它。仅仅用一次然后缓存结果
· 使用正确的数据结构来存储数据
· 使用`rowHeight`, `sectionFooterHeight` 和`sectionHeaderHeight`来设定固定的高。不要请求delegate
21. 选择正确的数据存储选项
当存储大块数据时你会怎么做?
你有非常多选择。比方:
· 使用`NSUerDefaults`
· 使用XML, JSON, 或者 plist
· 使用NSCoding存档
· 使用类似SQLite的本地SQL数据库
· 使用 Core Data
NSUserDefaults的问题是什么?尽管它非常nice也非常便捷。可是它仅仅适用于小数据,比方一些简单的布尔型的设置选项,再大点你就要考虑其他方式了
XML这样的结构化档案呢?整体来说,你须要读取整个文件到内存里去解析,这样是非常不经济的。
使用SAX又是一个非常麻烦的事情。
NSCoding?不幸的是,它也须要读写文件。所以也有以上问题。
在这样的应用场景下,使用SQLite 或者 CoreData比較好。使用这些技术你用特定的查询语句就能仅仅载入你须要的对象。
在性能层面来讲。SQLite和CoreData是非常相似的。
他们的不同在于详细用法。Core Data代表一个对象的graphmodel,但SQLite就是一个DBMS。
Apple在普通情况下建议使用CoreData,可是假设你有理由不使用它,那么就去使用更加底层的SQLite吧。
假设你使用SQLite,你能够用FMDB(https://GitHub.com/ccgus/fmdb)这个库来简化SQLite的操作,这样你就不用花非常多经历了解SQLite的C API了。
进阶性能提示
想要一些是你成为程序员忍者的精英级的建议吗?以下这些提示能够帮你把你的app优化到极致!
22. 加速启动时间
高速打开app是非常重要的,特别是用户第一次打开它时,对app来讲。第一印象太太太重要了。
你能做的就是使它尽可能做很多其它的异步任务,比方载入远端或者数据库数据,解析数据。
还是那句话。避免过于庞大的XIB。由于他们是在主线程上载入的。
所以尽量使用没有这个问题的Storyboards吧。
注意,用Xcodedebug时watchdog并不执行。一定要把设备从Xcode断开来測试启动速度
23. 使用Autorelease Pool
`NSAutoreleasePool`负责释放block中的autoreleasedobjects。
普通情况下它会自己主动被UIKit调用。
可是有些状况下你也须要手动去创建它。
假如你创建非常多暂时对象。你会发现内存一直在降低直到这些对象被release的时候。
这是由于仅仅有当UIKit用光了autoreleasepool的时候memory才会被释放。
好消息是你能够在你自己的@autoreleasepool里创建暂时的对象来避免这个行为:
1 2 3 4 5 6 7 8 9 |
NSArray *urls = <# An array of file URLs #>; for(NSURL *url in urls) { @autoreleasepool { NSError *error; NSString *fileContents = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error]; /* Process the string, creating and autoreleasing more objects. */ } }
|
这段代码在每次遍历后释放全部autorelease对象
很多其它关于NSAutoreleasePool请參考官方文档。
24. 选择是否缓存图片
常见的从bundle中载入图片的方式有两种,一个是用`imageNamed`,二是用`imageWithContentsOfFile`,第一种比較常见一点。
既然有两种类似的方法来实现同样的目的,那么他们之间的区别是什么呢?
`imageNamed`的长处是当载入时会缓存图片。`imageNamed`的文档中这么说:
这种方法用一个指定的名字在系统缓存中查找并返回一个图片对象假设它存在的话。
假设缓存中没有找到对应的图片,这种方法从指定的文档中载入然后缓存并返回这个对象。
相反的。`imageWithContentsOfFile`仅载入图片。
以下的代码说明了这两种方法的使用方法:
1 2 3 |
UIImage *img = [UIImage imageNamed:@"myImage"];// caching // or UIImage *img = [UIImage imageWithContentsOfFile:@"myImage"];// no caching
|
那么我们应该怎样选择呢?
假设你要载入一个大图片并且是一次性使用,那么就不是必需缓存这个图片,用`imageWithContentsOfFile`足矣。这样不会浪费内存来缓存它。
然而,在图片重复重用的情况下`imageNamed`是一个好得多的选择。
25. 避免日期格式转换
假设你要用`NSDateFormatter`来处理非常多日期格式。应该小心以待。就像先前提到的,不论什么时候重用`NSDateFormatters`都是一个好的实践。
然而,假设你须要很多其它速度,那么直接用C是一个好的方案。
SamSoffes有一个不错的帖子(http://soff.es/how-to-drastically-improve-your-app-with-an-afternoon-and-instruments)里面有一些能够用来解析ISO-8601日期字符串的代码。简单重写一下就能够拿来用了。
嗯,直接用C来搞。看起来不错了,可是你相信吗,我们还有更好的方案。
假设你能够控制你所处理的日期格式。尽量选择Unix时间戳。你能够方便地从时间戳转换到NSDate:
1 2 3 |
- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp { return[NSDate dateWithTimeIntervalSince1970:timestamp]; }
|
这样会比用C来解析日期字符串还快。
须要注意的是,很多web API会以微秒的形式返回时间戳,由于这样的格式在javascript中更方便使用。
记住用`dateFromUnixTimestamp`之前除以1000就好了。
很多其它阅读
下列这些WWDC视频强烈推荐给想要提高app性能的开发人员。
你首先须要保证你有使你的AppleID注冊为一个开发人员身份才干看在这里看WWDC2012的视频。
· #406: Adopting Automatic Reference Counting
· #238: iOS App Performance: Graphics and Animations
· #242: iOS App Performance: Memory
· #235: iOS App Performance: Responsiveness
· #409: Learning Instruments
· #706: Networking Best Practices
· #514: OpenGL ES Tools and Techniques
· #506: Optimizing 2D Graphics and AnimationPerformance
· #601: Optimizing Web Content in UIWebViews andWebsites on iOS
· #225: Up and Running: Making a Great Impressionwith Every Launch
一些01年的WWDC视频也非常有价值:
· #308: Blocks and Grand Central Dispatch inPractice
· #323: Introducing Automatic Reference Counting
· #312: iOS Performance and Power Optimizationwith Instruments
· #105: Polishing Your App: Tips and tricks toimprove the responsiveness and performance
· #121: Understanding UIKit Rendering
其他一些值得看的视频,大部分来自iOS 5Tech Talks:
· Your iOS App Performance Hitlist
· Optimizing App Performance with Instruments
· Understanding iOS View Compositing
基于《YouriOS App Performance Hitlist》这个Michael Jurewitz的视频,OleBegemann写了一篇文字总结的文章。
Apple提供了一个很实用的叫做“Performance Tuning | 性能调优”的资源。