iOS性能优化

    这篇文章来自iOS Tutorial Team 成员 Marcelo Fabri, 他是 Movile 的一个iOS开发者. Check out his personal website or follow him on Twitter.原文地址javascript

     当咱们开发iOS应用时,好的性能对咱们的App来讲是很重要的。你的用户也但愿如此,可是若是你的app表现的反应迟钝或者很慢也会伤害到你的审核。html

     然而,因为IOS设备的限制有时很难工做得很正确。咱们开发时有不少须要咱们记住这些容易忘记的决定对性能的影响。java

     这是为何我写这篇文章的缘由。这篇文章用备忘录的形式集合了25个技巧和诀窍能够用来提升你的app性能。因此保持阅读来给你将来的App一个很不错的提升。ios

      Note:在优化代码以前,必须保证有个须要解决的问题!不要陷入"pre-optimizing(预优化)"你的代码。勤 用Instruments分析你的代码,发现任何一个须要提升的地方。Matt Galloway写了一个使用Instruments优化代码的的教程git

   

    如下这些技巧分为三个不一样那个的级别---基础,中级,高级。github

   基础web

   这些技巧你要老是想着实如今你开发的App中。数据库

   1. 用ARC去管理内存(Use ARC to Manage Memory)json

   2.适当的地方使用reuseIdentifier(Use a reuseIdentifier Where Appropriate)数组

   3.尽量设置视图为不透明(Set View as Opaque When Possible)

   4.避免臃肿的XIBs文件(Avoid Fat XiBs)

   5.不要阻塞主进程(Don't Block the Main Thread)

   6.调整图像视图中的图像尺寸(Size Images to Image Views)

   7.选择正确集合(Choose the Correct Collection)

   8.启用Gzip压缩(Enable GZIP Compression)

  

   中级

   这些技巧是当你遇到更复杂的状况的时候使用。

    9. 重用和延迟加载视图(Reuse and Lazy Load Views)

   10.缓存,缓存,缓存(Cache,Cache,Cache)

   11.考虑绘图(Consider Drawing)

   12.处理内存警告(Handle Memory Warnings)

   13.重用大开销对象(Reuse Expensive Objects)

   14.使用精灵表(Use Sprite Sheets )

   15.避免重复处理数据(Avoid Re-Processing Data)

   16.选择正确的数据格式(Choose the Right Data Format)

   17.适当的设置背景图片(Set  Background Images Appropriately)

   18.减小你的网络占用(Reduce Your Web Footprint)  

   19.设置阴影路径(Set the Shadow Path )

   20.你的表格视图Optimize Your Table Views)

   21.选择正确的数据存储方式(Choose Correct Data Storage Option)

  

   高级

   这些技巧你应该只在你很积极认为它们能解决这个问题,并且你以为用它们很温馨的时候使用。

   22.加速启动时间(Speed up Launch Time )

   23.使用自动释放池(Use AutoRelease Pool)

   24.缓存图像(Cache Images-Or not )

   25.尽量避免日期格式化器(Avoid Date Formatters Where Possible)  

   没有其余的,一块儿去看看这些技巧吧!

  

 基础的性能提高

1)用ARC去管理内存

   ARC是伴随IOS5 一块儿发布的,它用来消除常见的的内存泄漏。

   ARC是"Automatic Reference Counting"的缩写。它自动管理你代码中的retain/release循环,这样你就没必要手动作这事儿了。

   下面这段代码展现了建立一个view的经常使用代码

 

UIView *view =[[UIView alloc] init];
//...
[self.view addSubview:view];
[view release];

   这里极其容易忘记在代码结束的地方调用release,ARC将会自动的,底层的为你作这些工做。

   除了帮助你你避免内存泄漏,ARC还能保证对象再也不使用时立马被回收来提升你的性能。你应该在你的工程里多用ARC。

   这里是一些学习更多关于ARC的很是棒的资源

   值得注意的是ARC不能消除全部的内存泄漏。你依然有可能内存泄漏,这主要多是因为blocks(块),引用循环,CoreFoundation对象管理不善(一般是C结构体,或者是确实很糟糕的代码)。

 

2)适当的地方使用reuseIdentifier   

     在app开发中的一个常见的为UITableViewCells,UICollectionViewCells,UITableViewHeaderFooterViews设置一个正确的reuseIdentifier(重用标识)。

 

 

     为了最大化性能,一个tableView的数据源通常应该重用UITableViewCell对象,当它在tableView:cellForRowAtIndexPath:中分配数据给cells的时候。一个表视图维护了一个UITableViewCell对象的队列或者列表,这些对象已被数据源标记为重用。

      若是你不用reuseIdentifier 会怎么样呢?

     若是你用,你的tableview每显示一行将会配置一个全新的cell。这是很是费事的操做并且绝对会影响你app滚动的性能。

     自从引进了iOS6,你应该为header and footer 视图设置reuseIdentifiers,就像在 UICollectionView’s cells 和 supplementary views(补充视图)同样。

     使用reuseIdentifiers,当你的数据源要求提供一个新的cell给tableview的时候调用这个方

NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

 

3)可能的时候设置视图为不透明

     若是你有不透明视图(opaque views)--也就是说,没有透明度定义的视图,你应该设置他们的opaque属性为YES。

     为何? 这会容许系统以最优的方式绘制你的views。这是一个简单的属性能够在Interface Builder 和代码中设置。

     苹果的文档 Apple documentation中有对这个属性的描述

     这个属性提供了一个提示给图系统如何对待这个视图。若是设置为YES,绘制系统将会把这个视图视为彻底不透明。这样容许系统优化一些绘制操做和提升性能。若是设置为NO,绘图系统会复合这个视图和其余的内容,这个属性的默认值是YES

    在相对静态的屏幕上,设置opaque属性不会有什么大问题。尽管如此,若是你的视图是嵌入在一个scrollView,或者是一个复杂的动画的一部分,不设置这个属性绝对会影响你的程序的性能。

    你也可使用Debug\Color olor Blended Layers选项 在你的模拟器中形象化的看见没有设置为不透明(opaque)的视图.你的目标应该是尽量多的设置视图为透明。

 

4)  避免臃肿的XIB文件

     故事板,由iOS5引进,很快的替代XIBs。尽管如此,XIBs在一下状况下依然是颇有用的。若是你须要在IOS5以前版本的设备上运行或者你想自定义重用的视图,那么你确实不能避免使用它们。

     若是你专一使用XIBs,那么让它们尽可能的简单。尝试为一个试图控制器建立一个XIB,若是可能的话,把一个视图控制器的视图分层管理在单独的XIBs中。

    注意当你加载一个XIB到内存的时候,它全部的内容都会载入内存,包括全部的图片。若是你有视图但不是要当即使用,那你就浪费了珍贵的内存。值得注意的是这不会发生在故事板中,由于故事版只会在须要的时候实例化一个视图控制器。

    当你载入一个xib,全部的图像文件会被缓存,若是是开发OSX,那么音频文件也会被缓存。

    Apple’s documentation 如是说:

    当你载入一个包含了图和声音资源引用的nib文件时,nib加载代码读取实际的图片文件和音频文件到内存中并缓存它。在OS X中,图片和音频资源被存储在已命名的缓存 中这样你能够在以后须要的时候访问它们。在iOS中,只有图片资源被缓存,访问图片,你使用NSImage或者UIImage的imageNamed:方法来访问,具体使用取决于你 的平台。

    显然这也发生在使用故事板的时候。尽管如此,我还不能找到这种说法的证据。若是你知道,请给我留言。

    想学习更多关于故事板的更多内容吗?看看Matthijs Hollemans的 Beginning Storyboards in iOS 5 Part 1and Part 2.

 

5)不要阻塞主进程

    你永远不该该在主线程中作任何繁重的工做。这是由于UIKIt的全部工做都在主线程中进行,好比绘画,管理触摸,和响应输出。

你的app的全部工做都在主线程上进行就会有阻塞主线程的风险,你的app会表现的反应迟钝。这是在App Store里获一星评论的快速途径!(做者卖萌..)

    阻塞主线程最多的状况就是发生在你的app进行I/O操做,包括牵扯到任何须要读写外部资源的任务,好比读取磁盘或者网络

    你能够异步的执行网络任务使用NSURLConnection中的这个方法:

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler

     或者使用第三方框架好比 AFNetworking.

    若是你在作任何大开销的操做(好比执行一个耗时的计算,或者读写磁盘)使用Grand Central Dispatch(GCD)或者 NSOperations 和 NSOperationQueues.

    使用GCD的模板以下代码所示:

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的详情感兴趣?看看Ray Wenderlich’s Multithreading and Grand Central Dispatch on iOS for Beginners 教程,和 Soheil Azarpour’s How To Use NSOperations and NSOperationQueues 教程。

 

 

6)调整图像视图中的图像尺寸

    若是你用UIImageView呈现app束中的图片时,确认图片和UIImageView的尺寸相同。缩放图片会很是的耗时,特别是当你的UIImageView被嵌入UIScrollView。

     若是图片是从远程服务器上下载的,有时你无法控制图片尺寸,或者你不能在服务器上在下载以前缩放它。在这些状况下你能够在图片下载完成后手动缩放一次,最好是在后台进程中。然在UIImageView中使用调整尺寸以后的图片。

 

7)选择正确集合

     学着怎么在手头工做中使用最合适的类或对象是写出高效代码的基本。当时用集合是(collections),这个说法特别对。

     可喜的是在苹果开发者文档( Collections Programming Topics)中有详细解释可用类之间的关系,还有解释各个类的适用状况。这个文档是每一个使用集合的人的必读文档。

    这是一个最多见的集合类型的快速简介:

  • Arrays:有序的值的列表,用index快速查找,经过值查找慢,insert/delete操做慢。
  • Dictionaries:存储键/值对.用index快速查找。
  • Sets: 无序的值列表。经过值快速查找,insert/delete快。

 

8)启用Gzip压缩

     大量和持续增加的app依赖从远端服务器或者外部APIs获取的外部数据。某些时候你可能会开发一些须要下载XML,JSON,HTML或者其余文本格式的应用。

     问题是移动设备不能保证网络环境,用户可能一分钟在边缘网络,下一分钟又是3G网络,不管什么状况下,你不想你的用户一直等待。

     一个减小文件大小并加速下载的网络资源的方法是同时在你的服务器和客户端上使用GZIP压缩,对于文本数据这种有高比率压缩的数据来讲很是有用。

     好消息是iOS早已默认支持GZIP压缩,若是你是使用NSURLConnection或者创建在这之上的框架好比AFNetworking。更好的消息是一切云服务提供商像 Google App Engine早已发送压缩以后的响应数据。

     这里有一篇文章great article about GZIP compression 介绍如何在你的Apache或IIS服务器上启用GZIP。

 

中级性能提高

    好的,当谈到优化你的代码时,你应该很自信你已经初级的方法已经彻底掌握了。但有时候有的问题的解决方法并非那么显而易见,它由你app的结构和代码决定,尽管如此,在正确的上下文中,它们多是没有价值的。

9)重用和延迟加载视图
  

     越多的视图就有越多的绘图操做,最终意味着更多的CPU和内存开销。这说得特别对若是你的app嵌入不少视图在UIScrollView时。

     管理这个的技巧是去模拟UITableView 和 UICollectionView的行为:不要一次建立全部的子视图,而是在须要的时候建立,而后把他们假如重用队列中。

     这样,你只须要在视图浮动时配置你的视图,避免昂贵的资源分配开销。

     视图建立的时机问题也一样适用于你app的其余地方。试想当你点击一个button时呈现一个视图的情景。至少有两种方法:

     1.屏幕第一次载入时建立视图并隐藏它。当你须要的时候,显示出来。

     2.须要呈现的时候一次建立视图并显示它。

     每种方法都有各自的优缺点

     使用第一种方法,你消耗了更多内存由于从建立开始到它释放前你都保持了它的内存,然而,当你点击button的时候,你的app会表现得响应快速由于它只须要更改视图的可视化属性。

     使用第二种方法会有相反的效果,在须要的时候建立视图,消耗更少的内存,但当button被点击时应用会表现得不那么响应快速。

 

10)缓存,缓存,缓存

 

      在开发应用时的一个伟大的经验是"Cache what matters"--也就是说那些不大会改变但会平凡被访问的东西。

     你能缓存些什么呢?缓存的候选项有远程服务器的响应,图片,已计算过的值(好比UITableView的行高)。

      NSURLConnection 根据处理的Http头缓存资源到磁盘或者内存中,你甚至能够手动建立一个NSURLRequest值加载缓存过的值。

      这里有一段很棒的代码,用在任什么时候候你须要针对一个不大会改变的图片建立一个NSURLRequest。

+ (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"];

    return request;

}

      若是想知道更多关于Http caching,NSURLCache,NSURLConnection等内容,请阅读the NSURLCache entry

      注意,你能够经过NSURLConnection获取取一个URL请求,AFNetworking也能够。有了这个技巧这样你不用改变任何你的网络代码。

      若是要缓存不牵扯到HTTP请求的其余东西,NSCache是很好的选择。

      NSCache像NSDictionary,可是当系统须要回收内存的时候会自动的移除内容。

      对HTTP Cache感兴趣并想学更多的内容?推荐阅读这篇文章best-practices document on HTTP caching

 

11)考虑绘图

    在IOS中有不少方法能够制做拥有很棒外观的buttons,你能够是由全尺寸的图像,也可使用调整尺寸以后的图像,或者你用CALayer,CoreGraphics,甚至OpenGL手动的它们。

    固然,每种途径都有不一样的复杂度级别和不一样的性能,这篇文章很是值得一读post about iOS graphics performance here,这是Apple UIKit团队成员Andy Matuschak发表的文章,里面对各类方法有一些很是棒的看法和对性能的权衡。

     使用预渲染图片更快,由于iOS不用建立一张图像和绘制图形到屏幕上(图像已经处理好了)。问题是你须要所有把这些图片放进应用束里,增长它的尺寸。那就是为何使用可调整尺寸的图片是那么好:你经过移除”浪费了的“图片空间来节约空间。你也不须要为不一样的元素生成不一样的图片。(例如 buttons)

    尽管如此,用图片你会失去代码调整你图片的能力,须要一次又一次的生成它们而后把它们加入到应用中。这是个缓慢的过程。另一点若是你有动画或者不少张稍微变化的图片(例如 颜色叠加),你须要加不少的图片增长了应用束的大小。

     总结一下,你须要想对你来讲最重要的是什么:绘图性能仍是app的大笑.一般两个都很重要,因此你会在一个工程里使用这两种方法。

 

12)处理内存警告

      当系统内存低的时候iOS会通知全部的正在运行的app,关于低内存警告的处理苹果官方文档 official Apple documentation描述:

      若是你的应用收到这个警告,它必须尽量多的释放内存。最好的方法是移除对缓存,图像对象,和其余稍后要建立的对象的强引用。

      幸运的是,UIKit提供了一些方法去接收低内存警告:

  • 实现App代理中的applicationDidReceiveMemoryWarning:方法。
  • 重载你自定义UIViewController子类中的didReceiveMemoryWarning方法。
  • 注册接收UIApplicationDidReceiveMemoryWarningNotification的通知

      一旦收到这些警告,你的处理方法必须马上响应并释放没必要要的内存。

      举例,若是视图当前不可见,UIViewController的默认行为是清除这些视图;子类能够经过清除额外的数据结构来补充父类的默认行为。一个应用程序维护一个图片的缓存,没有在屏幕上的图片都会被释放。

     一旦收到内存警告,释放可能的所有内存是很重要的,不然你就有让你的app被系统杀死的的风险。

     尽管如此,开始扑杀对象释放内存的时候要当心,由于你须要保证它们会在以后从新建立。当你开发app的时候,用你的模拟器上的模拟内存警告功能测试这种状况。

 

13)重用大开销对象

   有的对象的初始化很是慢--NSDateFormatter 和 NSCalendar是两个例子,可是你不能避免使用它们,当你从 JSON/XML响应中解析日期时。

   避免使用这些对象时的性能瓶颈,试着尽量的重用这些对象。你能够加入你的类中成为一个属性,也能够建立为静态变量。

   注意若是你选择了第二种方式,这个对象在app运行的时候会一直保持在内存里,像单例同样。

  下面这段代码演示了NSDateFomatter做为一个属性的lazy加载,第一次被调用而后建立它,以后就使用已建立在的实例

// 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的日期格式几乎跟建立一个新的同样慢。所以,若是在你的应用中你频繁须要处理多个日期格式,你的代码应该获利于初始化建立,重用,多个NSDateFormatter对象。

14) 使用精灵表

    你是一个游戏开发者吗?精灵表是你的好朋友之一.精灵表让绘制比标准屏幕绘制方法更快速,消耗更少的内存。

    这里有两个很棒的精灵表使用的教程

  1. How To Use Animations and Sprite Sheets in Cocos2D
  2. How to Create and Optimize Sprite Sheets in Cocos2D with Texture Packer and Pixel Formats

    第二个教程详细覆盖了像素格式,它能够对游戏性能有一个可衡量的影响。

    若是对精灵表还不是很熟悉,一个很好的介绍 SpriteSheets – The Movie, Part 1and Part 2. 这些视频的做者是Andreas Löw,一个最流行的建立精灵表的工具Texture Packer的建立者。

     除了使用精灵表以外,以前已经说到的内容也能够用在游戏上.举个例子,若是你的游戏有不少精灵,好比在标准的敌人或炮弹射击游戏,你能够重用精灵表额如是每次从新建立它们。

 

15)避免重复处理数据

     不少app调用函数获取远程服务器上的数据.这些数据一般是经过JSON 或者 XML格式来传输。很是重要的是在请求和接收数据的时候努力在两端使用相同的数据结构。

     理由?在内存中操纵数据以合适你的数据结构是很是昂贵的。

     好比,若是你须要在表格视图中显示数据,最好请求和接收数据是数组的格式,以免任何中间操纵数据,使其适合你在app中使用的数据结构

     类似的,若是你的应用程序依赖于访问特定值的键,那么你可能会想要请求和接收一个键/值对的字典

     经过第一次就获取正确格式的数据,在本身的应用程序中你就会避免不少的重复处理工做,使数据符合你的选择的结构。 

 

16)选择正确的数据格式

    你能够有不少方法从web 服务中传递数据到你的app中   

    JSON 是一种一般比XML小且解析更快的格式,它的传输的内容也比较小。自iOS5起,内置的JSON解析很好用 built-in JSON deserialization

    尽管如此,XML的一个优点当你使用SAXparsing方法时,你能够传输过程当中读取它,在面的很是大的数据时,你没必要像JSON同样在数据下载完以后才开始读取。

 

17)适当的设置背景图片

    像iOS编码的其余工做同样,至少有两种不一样方式去替换你视图的背景图片。

  1. 你能够设置你的视图的背景颜色为UIColor的colorWithPatternImage建立的颜色。
  2. 你能够添加一个UIImageView子试图给View

    若是你有全尺寸的背景图片,你绝对要用UIImageView,由于UIColor的colorWithPatternImage是重复的建立小的模式图片,在这种状况下用UIImageView方式会节约不少内存。

// You could also achieve the same result in Interface Builder

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

[self.view addSubview:backgroundView];

     尽管如此,若是你计划用模式图片背景,你应该是用UIColor的colorWithPatternImage。它更快一些,并且这种状况不会使用不少内存。

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


18)减小你的网络占用

    UIWebView 是很是游泳的.它很是容易用来显示web内容,甚至建立你app的视窗。这些都是标准UIKit 空间很难作到的。

    尽管如此,你可能注意你能够用在你的app中的UIWebView组件并无Apple的Safari app快。这是Webkit’s的Nitro引擎的限制使用。JIT compilation.

     因此为了得到最佳的性能,你须要调整你的HTML。第一件事是尽量多的避免Javascript,包括避免大的框架好比jQuery。有时使用vanilla Javascript取代依赖的框架会快不少。

     随时随地遵循异步加载Javascript文件的实践。特别当它们不直接影响到页面表现的时候,好比分析脚本。

    最后,老是要意识到你在用的图片,保持图片的正确尺寸。正如这个教程前面所提到的,利用精灵表的优点来节约内存和提升速度。

     想要获取更多的信息,看看WWDC 2012 session #601 – Optimizing Web Content in UIWebViews and Websites on iOS.

 

19) 设置阴影路径

     你须要给视图或者layer添加一个阴影,你应该怎么作?

     大多数开发者是添加 QuartzCore框架到工程中,而后写以下代码:

#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;

      看起来很是简单,是吧?

      很差的是这个方法有一个问题。核心动画必需要先作一幕动画肯定视图具体形状以后才渲染阴影,这是很是费事的操做。

      这里有个替代方法让系统更好的渲染,设置阴影路径:

view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath];


     若是你想知道这个内容的更多技巧,Mark Pospesel 写过一篇post about shadowPath.      

     设置阴影路径,iOS不须要老是计算如何绘制阴影。而是用已经计算好的的路径。坏消息是它依赖与你的视图格式,你是视图可能很难计算这个路径。另外一个问题是你须要在每次视图的框架改变时更新阴影路径。

 

20) 优化你的表格视图

    表格视图须要快速的滚动,若是不能,用户能确切注意到很滞后。

    为了让你的表格视图流畅的滚动,保证你实现了下列的建议。

  • 经过正确的reuseIdentifier重用cells
  • 尽可能多的设置views 为不透明,包括cell自己。
  • 避免渐变,图像缩放,屏幕之外的绘制。
  • 若是行高不老是同样,缓存它们。
  • 若是cell显示的内容来自网络,确保异步和缓存。
  • 使用shadowPath来创建阴影。
  • 减小子视图的数目。
  • cellForRowAtIndexPath:中作尽可能少的工做,若是须要作相同的工做,那么只作一次并缓存结果。
  • 使用适当的数据结构存储你要的信息,不一样的结构有对于不一样的操做有不一样的代价。
  • 使用rowHeight,sectionFooterHeight,sectionHeaderHeight为常数,而不是询问代理。

 

21) 选择正确的数据存储方式

   当要存储和读取大数据的时候你的选择是什么?

   你有一些选项,包括:

  • 使用 NSUserDefaults存储它们。
  • 存储在结构化文件中,XML,JSON,Plist格式中。
  • 是用NSCoding打包?
  • 存储在本地数据库,如SQLite
  • 使用NSData

     NSUserDefaults有什么问题呢?虽说NSUserDefaults是好并且简单,它确实很好只有当你有不多的数据要存(像你的等级,或者音量是开仍是关)。一旦你接触大数据,会有更好的其余选择。

     保存在结构化文件中也可能有问题。通常的,在解析以前,你须要加载整个文件到内存中,这是很是耗时的操做。你可使用SAX去处理XML文件,可是那是一个复杂的做法。同时你加载了所有的对象进内存,其中有你想要的也有不想要的。

     那么NSCoding怎么样呢?不幸的是,它也一样要读写文件,跟上面说的方法有一样的问题。

     你最好的解决方法是使用SQLite或者 Core Data. 经过这些技术,你能够执行特定的查询只加载须要的对象,避免强力搜索方法来检索数据。性能方面,SQLite和Core Data 很是接近。

    SQLite 和 Core Data最大的不一样就是它们的使用方法。Core Data呈现为一个对象图模型,可是SQLite是一个传统的DBMS(数据库管理系统).一般Apple建议你用Core Data,可是除非你有特殊的缘由不让你你会想避开它,使用更低级的SQLite。

    若是在你的app中使用SQLite,一个方便的库 FMDB 容许你使用SQLite而不用专研SQLite的C API。

 

高级性能技巧

   寻找一些精英的方式去成为十足的代码忍者?这些高级性能技巧能够合适的时候使用让你的app运行得尽量的高效。

22)加速启动时间

   App的启动时间很是重要,特别是第一次启动的时候。第一影响意味着太多了!

   最大的事情是保证你的App开始尽可能的快,尽可能的多的执行异步任务,不如网络请求,数据库访问,或者数据解析。

   尽可能避免臃肿的XIBs,由于你在主线程中加载。可是在故事板中不会有这个问题,因此尽可能用它们。

   Note: 监察人不会运行你的app在Xcode调试中, 因此确保测试启动性能时断开与Xcode的链接。

 

23)使用自动释放池

     NSAutoreleasePool负责释放在代码块中的自动释放对象。一般,它是被UIKit自动调用的。可是也有一些场景咱们须要手动建立NSAutoreleasePools。

     举个例子,若是你建立太多的临时对象在你的代码中,你会注意到你的内存用量会增长直到对象被释放掉。问题是内存只有在UIKit排空(drains)自动释放池的时候才能被释放,这意味着内存被占用的时间超过了须要。               

     好消息是你能够在你的@autoreleasepool段中建立临时对象来避免上述状况。代码以下所示。

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. */

    }

}

    在每次迭代以后会自动释放全部的对象。

   

    你能够阅读更多关于NSAutoreleasePool的内容Apple’s official documentation.

 

24)缓存图像

     这里有两种方法去加载app束中的Image,第一个常见的方式是用imageNamed. 第二个是使用imageWithContentsOfFile

     为何会有两种方法,它们有效率吗?

 

     imageNamed 在载入时有缓存的优点。文档 documentation for imageNamed是这样解释的:

     这个方法看起来在系统缓存一个图像对象并指定名字,若是存在则返回对象,若是匹配图像的对象不在缓存中,这个方法会从指定的文件中加载数据,并缓存它,而后返回结果对象。

    做为替代,imageWithContendsOfFile 简单的载入图像并不会缓存。

    这两个方法的的演示片断以下:

UIImage *img = [UIImage imageNamed:@"myImage"]; // caching

// or
UIImage *img = [UIImage imageWithContentsOfFile:@"myImage"]; // no caching

 

     若是你加载只使用一次大图片,那就不须要缓存。这种状况imageWithContendsOfFile会很是好,这种方式不会浪费内存来缓存图片。何时使用哪种呢?

     然而,imageNamed 对于要重用的图片来讲是更好的选择,这种方法节约了常常的从磁盘加载图片的时间。

 

25) 尽量避免日期格式化器

 

     若是你要用NSDateFormatter来解析日期数据,你就得当心对待了。以前提到过,尽可能的重用NSDateFormatters老是一个好的想法。

     然而,若是你须要更快的速度,你可使用C代替NSDateFormatter来解析日期。 Sam Soffes写了一篇 blog post about this topic来讲明如何用代码来解析 ISO-8601日期串。尽管如此,你能够很容易的修改他的代码例子来适应你的特殊需求。

     噢,听起来很棒,可是你相信有更好的办法吗?

     若是你能控制你所处理日期的格式,尽量的选择使用 Unix timestamps。Unix时间戳是简单的整数表明从某个起始时间点开始到如今的秒数。这个起始点一般是1970年1月1日 UTC 00:00:00。

    你能够容易的把时间戳转换为NSDate,以下面所示:

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

  return [NSDate dateWithTimeIntervalSince1970:timestamp];

}

     这甚至比C函数更快

     注意,不少WEB APIs返回时间戳是毫秒,由于这对于javascript最终来使用和处理数据是很是常见的。只要记住将这个时间戳除以1000再传递给dateFromUnixTimestamp方法便可。

相关文章
相关标签/搜索