iOS app性能優(yōu)化

instruments

在iOS上進行性能分析的時候,首先考慮借助instruments這個利器分析出問題出在哪躯砰,不要憑空想象,不然你可能把精力花在了1%的問題上,最后發(fā)現(xiàn)其實啥都沒優(yōu)化沃但,比如要查看程序哪些部分最耗時,可以使用Time Profiler佛吓,要查看內(nèi)存是否泄漏了宵晚,可以使用Leaks等。關(guān)于instruments網(wǎng)上有很多資料维雇,作為一個合格iOS開發(fā)者淤刃,熟悉這個工具還是很有必要的。

目錄

我要給出的建議將分為三個不同的等級: 入門級吱型、 中級和進階級:

入門級(這是些你一定會經(jīng)常用在你app開發(fā)中的建議)

  1. 用ARC管理內(nèi)存
  2. 在正確的地方使用reuseIdentifier
  3. 盡可能使Views透明
  4. 避免龐大的XIB
  5. 不要block主線程
  6. 在Image Views中調(diào)整圖片大小
  7. 選擇正確的Collection
  8. 打開gzip壓縮

中級(這些是你可能在一些相對復(fù)雜情況下可能用到的)

  1. 重用和延遲加載Views
  2. Cache, Cache, 還是Cache逸贾!
  3. 權(quán)衡渲染方法
  4. 處理內(nèi)存警告
  5. 重用大開銷的對象
  6. 使用Sprite Sheets
  7. 避免反復(fù)處理數(shù)據(jù)
  8. 選擇正確的數(shù)據(jù)格式
  9. 正確地設(shè)定Background Images
  10. 減少使用Web特性
  11. 設(shè)定Shadow Path
  12. 優(yōu)化你的Table View
  13. 選擇正確的數(shù)據(jù)存儲選項

進階級(這些建議只應(yīng)該在你確信他們可以解決問題和得心應(yīng)手的情況下采用)

  1. 加速啟動時間
  2. 使用Autorelease Pool
  3. 選擇是否緩存圖片
  4. 盡量避免日期格式轉(zhuǎn)換

1.用ARC管理內(nèi)存

ARC(Automatic Reference Counting, 自動引用計數(shù))和iOS5一起發(fā)布,它避免了最常見的也就是經(jīng)常是由于我們忘記釋放內(nèi)存所造成的內(nèi)存泄露津滞。它自動為你管理retain和release的過程铝侵,所以你就不必去手動干預(yù)了。
下面是你會經(jīng)常用來去創(chuàng)建一個View的代碼段:

UIView *view = [[UIView alloc] init];
// ...
[self.view addSubview:view];
[view release];
忘掉代碼段結(jié)尾的release簡直像記得吃飯一樣簡單据沈。而ARC會自動在底層為你做這些工作哟沫。除了幫你避免內(nèi)存泄露,ARC還可以幫你提高性能锌介,它能保證釋放掉不再需要的對象的內(nèi)存嗜诀。這都啥年代了,你應(yīng)該在你的所有項目里使用ARC!

2.在正確的地方使用 reuseIdentifier

一個開發(fā)中常見的錯誤就是沒有給UITableViewCells孔祸, UICollectionViewCells隆敢,甚至是UITableViewHeaderFooterViews設(shè)置正確的reuseIdentifier。
為了性能最優(yōu)化崔慧,table view用 tableView:cellForRowAtIndexPath: 為rows分配cells的時候拂蝎,它的數(shù)據(jù)應(yīng)該重用自UITableViewCell。 一個table view維持一個隊列的數(shù)據(jù)可重用的UITableViewCell對象惶室。不使用reuseIdentifier的話温自,每顯示一行table view就不得不設(shè)置全新的cell。這對性能的影響可是相當大的皇钞,尤其會使app的滾動體驗大打折扣悼泌。
自iOS6起,除了UICollectionView的cells和補充views夹界,你也應(yīng)該在header和footer views中使用reuseIdentifiers
3.盡量把views設(shè)置為完全不透明
如果你有透明的Views你應(yīng)該設(shè)置它們的opaque(不透明)屬性為YES馆里。例如一個黑色半透明的可以設(shè)置為一個灰色不透明的View替代.原因是這會使系統(tǒng)用一個最優(yōu)的方式渲染這些views。這個簡單的屬性在IB或者代碼里都可以設(shè)定。
Apple的文檔對于為圖片設(shè)置透明屬性的描述是:
(opaque)這個屬性給渲染系統(tǒng)提供了一個如何處理這個view的提示鸠踪。如果設(shè)為YES丙者, 渲染系統(tǒng)就認為這個view是完全不透明的,這使得渲染系統(tǒng)優(yōu)化一些渲染過程和提高性能营密。如果設(shè)置為NO械媒,渲染系統(tǒng)正常地和其它內(nèi)容組成這個View。默認值是YES评汰。
在相對比較靜止的畫面中滥沫,設(shè)置這個屬性不會有太大影響。然而當這個view嵌在scroll view里邊键俱,或者是一個復(fù)雜動畫的一部分兰绣,不設(shè)置這個屬性的話會在很大程度上影響app的性能。

換種說法编振,大家可能更好理解:

只要一個視圖的不透明度小于1,就會導致blending.blending操作在iOS的圖形處理器(GPU)中完成的,blending主要指的是混合像素顏色的計算缀辩。舉個例子,我們把兩個圖層疊加在一起,如果第一個圖層的有透明效果,則最終像素的顏色計算需要將第二個圖層也考慮進來。這一過程即為Blending踪央。

為什么Blending會導致性能的損失臀玄?

原因是很直觀的,如果一個圖層是完全不透明的,則系統(tǒng)直接顯示該圖層的顏色即可。而如果圖層是帶透明效果的,則會引入更多的計算,因為需要把下面的圖層也包括進來,進行混合后顏色的計算畅蹂。

4. 避免過于龐大的XIB

iOS5中加入的Storyboards(分鏡)正在快速取代XIB健无。然而XIB在一些場景中仍然很有用。比如你的app需要適應(yīng)iOS5之前的設(shè)備液斜,或者你有一個自定義的可重用的view,你就不可避免地要用到他們累贤。
如果你不得不XIB的話,使他們盡量簡單少漆。嘗試為每個Controller配置一個單獨的XIB臼膏,盡可能把一個View Controller的view層次結(jié)構(gòu)分散到單獨的XIB中去。
需要注意的是示损,當你加載一個XIB的時候所有內(nèi)容都被放在了內(nèi)存里渗磅,包括任何圖片。如果有一個不會即刻用到的view检访,你這就是在浪費寶貴的內(nèi)存資源了始鱼。Storyboards就是另一碼事兒了,storyboard僅在需要時實例化一個view controller.
當你加載一個引用了圖片或者聲音資源的nib時脆贵,nib加載代碼會把圖片和聲音文件寫進內(nèi)存医清。在OS X中,圖片和聲音資源被緩存在named cache中以便將來用到時獲取丹禀。在iOS中状勤,僅圖片資源會被存進named caches。取決于你所在的平臺双泪,使用NSImage 或UIImage 的imageNamed:方法來獲取圖片資源持搜。

5. 不要阻塞主線程

永遠不要使主線程承擔過多。因為UIKit在主線程上做所有工作焙矛,渲染葫盼,管理觸摸反應(yīng),回應(yīng)輸入等都需要在它上面完成村斟。一直使用主線程的風險就是如果你的代碼真的block了主線程贫导,你的app會失去反應(yīng)

大部分阻礙主進程的情形是你的app在做一些牽涉到讀寫外部資源的I/O操作,比如存儲或者網(wǎng)絡(luò)蟆盹『⒌疲或者使用像 AFNetworking這樣的框架來異步地做這些操作。
如果你需要做其它類型的需要耗費巨大資源的操作(比如時間敏感的計算或者存儲讀寫)那就用 Grand Central Dispatch逾滥,或者 NSOperation 和 NSOperationQueues.
你可以使用NSURLConnection異步地做網(wǎng)絡(luò)操作:

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

6. 在Image Views中調(diào)整圖片大小

如果要在UIImageView中顯示一個來自bundle的圖片峰档,你應(yīng)保證圖片的大小和UIImageView的大小相同。在運行中縮放圖片是很耗費資源的寨昙,特別是UIImageView嵌套在UIScrollView中的情況下讥巡。
如果圖片是從遠端服務(wù)加載的你不能控制圖片大小,比如在下載前調(diào)整到合適大小的話舔哪,你可以在下載完成后欢顷,最好是用background thread,縮放一次捉蚤,然后在UIImageView中使用縮放后的圖片抬驴。

7. 選擇正確的Collection

學會選擇對業(yè)務(wù)場景最合適的類或者對象是寫出能效高的代碼的基礎(chǔ)。當處理collections時這句話尤其正確缆巧。

Apple有一個 Collections Programming Topics 的文檔詳盡介紹了可用的classes間的差別和你該在哪些場景中使用它們怎爵。這對于任何使用collections的人來說是一個必讀的文檔。
呵呵盅蝗,我就知道你因為太長沒看…這是一些常見collection的總結(jié):

Arrays: 有序的一組值鳖链。使用index來lookup很快,使用value lookup很慢墩莫, 插入/刪除很慢芙委。
Dictionaries: 存儲鍵值對。 用鍵來查找比較快狂秦。
Sets: 無序的一組值灌侣。用值來查找很快,插入/刪除很快裂问。

8. 打開gzip壓縮

大量app依賴于遠端資源和第三方API侧啼,你可能會開發(fā)一個需要從遠端下載XML, JSON, HTML或者其它格式的app牛柒。
問題是我們的目標是移動設(shè)備,因此你就不能指望網(wǎng)絡(luò)狀況有多好痊乾。一個用戶現(xiàn)在還在edge網(wǎng)絡(luò)皮壁,下一分鐘可能就切換到了3G。不論什么場景哪审,你肯定不想讓你的用戶等太長時間蛾魄。
減小文檔的一個方式就是在服務(wù)端和你的app中打開gzip。這對于文字這種能有更高壓縮率的數(shù)據(jù)來說會有更顯著的效用湿滓。好消息是滴须,iOS已經(jīng)在NSURLConnection中默認支持了gzip壓縮,當然AFNetworking這些基于它的框架亦然叽奥。像Google App Engine這些云服務(wù)提供者也已經(jīng)支持了壓縮輸出扔水。

9. 重用和延遲加載(lazy load) Views

更多的view意味著更多的渲染,也就是更多的CPU和內(nèi)存消耗朝氓,對于那種嵌套了很多view在UIScrollView里邊的app更是如此铭污。
這里我們用到的技巧就是模仿UITableView和UICollectionView的操作: 不要一次創(chuàng)建所有的subview,而是當需要時才創(chuàng)建膀篮,當它們完成了使命嘹狞,把他們放進一個可重用的隊列中。
這樣的話你就只需要在滾動發(fā)生時創(chuàng)建你的views誓竿,避免了不劃算的內(nèi)存分配磅网。
創(chuàng)建views的能效問題也適用于你app的其它方面。想象一下一個用戶點擊一個按鈕的時候需要呈現(xiàn)一個view的場景筷屡。有兩種實現(xiàn)方法:

創(chuàng)建并隱藏這個view當這個screen加載的時候涧偷,當需要時顯示它;
當需要時才創(chuàng)建并展示毙死。
每個方案都有其優(yōu)缺點燎潮。
用第一種方案的話因為你需要一開始就創(chuàng)建一個view并保持它直到不再使用,這就會更加消耗內(nèi)存扼倘。然而這也會使你的app操作更敏感因為當用戶點擊按鈕的時候它只需要改變一下這個view的可見性确封。
第二種方案則相反-消耗更少內(nèi)存,但是會在點擊按鈕的時候比第一種稍顯卡頓再菊。

10. Cache, Cache, 還是Cache!

一個極好的原則就是爪喘,緩存所需要的,也就是那些不大可能改變但是需要經(jīng)常讀取的東西纠拔。
我們能緩存些什么呢秉剑?一些選項是,遠端服務(wù)器的響應(yīng)稠诲,圖片侦鹏,甚至計算結(jié)果诡曙,比如UITableView的行高。
NSURLConnection默認會緩存資源在內(nèi)存或者存儲中根據(jù)它所加載的HTTP Headers略水。你甚至可以手動創(chuàng)建一個NSURLRequest然后使它只加載緩存的值价卤。
下面是一個可用的代碼段,你可以可以用它去為一個基本不會改變的圖片創(chuàng)建一個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"];

    returnrequest;

    }

注意你可以通過 NSURLConnection 獲取一個URL request聚请, AFNetworking也一樣的。這樣你就不必為采用這條tip而改變所有的networking代碼了稳其。
如果想了解更多關(guān)于HTTP caching, NSURLCache, NSURLConnection的相關(guān)知識驶赏,可以讀下這篇文章()
如果你需要緩存其它不是HTTP Request的東西,你可以用NSCache既鞠。
NSCache和NSDictionary類似煤傍,不同的是系統(tǒng)回收內(nèi)存的時候它會自動刪掉它的內(nèi)容。

11. 權(quán)衡渲染方法

在iOS中可以有很多方法做出漂亮的按鈕嘱蛋。你可以用整幅的圖片蚯姆,可調(diào)大小的圖片,uozhe可以用CALayer洒敏, CoreGraphics甚至OpenGL來畫它們龄恋。
當然每個不同的解決方法都有不同的復(fù)雜程度和相應(yīng)的性能。有一篇Apple UIKit team中的一員Andy Matuschak推薦過的很棒的關(guān)于graphic性能的帖子很值得一讀凶伙。

簡單來說郭毕,就是用事先渲染好的圖片更快一些,因為如此一來iOS就免去了創(chuàng)建一個圖片再畫東西上去然后顯示在屏幕上的程序函荣。問題是你需要把所有你需要用到的圖片放到app的bundle里面显押,這樣就增加了體積 – 這就是使用可變大小的圖片更好的地方了: 你可以省去一些不必要的空間,也不需要再為不同的元素(比如按鈕)來做不同的圖傻挂。
然而乘碑,使用圖片也意味著你失去了使用代碼調(diào)整圖片的機動性,你需要一遍又一遍不斷地重做他們金拒,這樣就很浪費時間了兽肤,而且你如果要做一個動畫效果,雖然每幅圖只是一些細節(jié)的變化你就需要很多的圖片造成bundle大小的不斷增大绪抛。
總得來說轿衔,你需要權(quán)衡一下利弊,到底是要性能能還是要bundle保持合適的大小睦疫。

12. 處理內(nèi)存警告

一旦系統(tǒng)內(nèi)存過低害驹,iOS會通知所有運行中app。在官方文檔中是這樣記述:
如果你的app收到了內(nèi)存警告蛤育,它就需要盡可能釋放更多的內(nèi)存宛官。最佳方式是移除對緩存葫松,圖片object和其他一些可以重創(chuàng)建的objects的strong references.
幸運的是,UIKit提供了幾種收集低內(nèi)存警告的方法:

在app delegate中使用applicationDidReceiveMemoryWarning: 的方法
在你的自定義UIViewController的子類(subclass)中覆蓋didReceiveMemoryWarning
注冊并接收 UIApplicationDidReceiveMemoryWarningNotification 的通知
一旦收到這類通知底洗,你就需要釋放任何不必要的內(nèi)存使用腋么。
例如,UIViewController的默認行為是移除一些不可見的view亥揖, 它的一些子類則可以補充這個方法珊擂,刪掉一些額外的數(shù)據(jù)結(jié)構(gòu)。一個有圖片緩存的app可以移除不在屏幕上顯示的圖片费变。
這樣對內(nèi)存警報的處理是很必要的摧扇,若不重視,你的app就可能被系統(tǒng)殺掉挚歧。
然而扛稽,當你一定要確認你所選擇的object是可以被重現(xiàn)創(chuàng)建的來釋放內(nèi)存。一定要在開發(fā)中用模擬器中的內(nèi)存提醒模擬去測試一下滑负。

13. 重用大開銷對象

一些objects的初始化很慢在张,比如NSDateFormatter和NSCalendar。然而矮慕,你又不可避免地需要使用它們帮匾,比如從JSON或者XML中解析數(shù)據(jù)。
想要避免使用這個對象的瓶頸你就需要重用他們痴鳄,可以通過添加屬性到你的class里或者創(chuàng)建靜態(tài)變量來實現(xiàn)辟狈。
注意如果你要選擇第二種方法,對象會在你的app運行時一直存在于內(nèi)存中夏跷,和單例(singleton)很相似哼转。
下面的代碼說明了使用一個屬性來延遲加載一個date formatter. 第一次調(diào)用時它會創(chuàng)建一個新的實例,以后的調(diào)用則將返回已經(jīng)創(chuàng)建的實例:

// 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;
}

還需要注意的是槽华,其實設(shè)置一個NSDateFormatter的速度差不多是和創(chuàng)建新的一樣慢的壹蔓!所以如果你的app需要經(jīng)常進行日期格式處理的話,你會從這個方法中得到不小的性能提升猫态。

14. 使用Sprite Sheets

你是一個游戲開發(fā)者嗎佣蓉,那么Sprite sheets一定是一個你的最好的朋友了。Sprite sheet可以讓渲染速度加快亲雪,甚至比標準的屏幕渲染方法節(jié)省內(nèi)存勇凭。
我們有兩個很好的關(guān)于Sprite的教程:

  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

第二個教程涵蓋了可能在很大程度上影響你游戲性能的pixel格式的細節(jié)。
如果你對于spirte sheet還不是很熟悉义辕,可以看下這兩個(youtube)視頻SpriteSheets – The Movie, Part 1 和Part 2虾标。視頻的作者是創(chuàng)建Sprite sheet很流行的工具之一Texture Packer的作者Andreas L?w。
除了使用Sprite sheets灌砖,其它寫在這里的建議當然也可以用于游戲開發(fā)中璧函。比如你需要很多的Sprite sheets傀蚌,像敵人,導彈之類的動作類必備元素蘸吓,你可以重用這些sprites而不用每次都要重新創(chuàng)建善炫。

15. 避免反復(fù)處理數(shù)據(jù)

許多應(yīng)用需要從服務(wù)器加載功能所需的常為JSON或者XML格式的數(shù)據(jù)。在服務(wù)器端和客戶端使用相同的數(shù)據(jù)結(jié)構(gòu)很重要库继。在內(nèi)存中操作數(shù)據(jù)使它們滿足你的數(shù)據(jù)結(jié)構(gòu)是開銷很大的箩艺。
比如你需要數(shù)據(jù)來展示一個table view,最好直接從服務(wù)器取array結(jié)構(gòu)的數(shù)據(jù)以避免額外的中間數(shù)據(jù)結(jié)構(gòu)改變。
類似的宪萄,如果需要從特定key中取數(shù)據(jù)艺谆,那么就使用鍵值對的dictionary。

16. 選擇正確的數(shù)據(jù)格式

從app和網(wǎng)絡(luò)服務(wù)間傳輸數(shù)據(jù)有很多方案雨膨,最常見的就是JSON和XML擂涛。你需要選擇對你的app來說最合適的一個读串。

  • 解析JSON會比XML更快一些聊记,JSON也通常更小更便于傳輸。從iOS5起有了官方內(nèi)建的JSON deserialization 就更加方便使用了恢暖。
  • 但是XML也有XML的好處排监,比如使用SAX 來解析XML就像解析本地文件一樣,你不需像解析json一樣等到整個文檔下載完成才開始解析杰捂。當你處理很大的數(shù)據(jù)的時候就會極大地減低內(nèi)存消耗和增加性能舆床。

17. 正確設(shè)定背景圖片

在View里放背景圖片就像很多其它iOS編程一樣有很多方法:

  1. 使用UIColor的 colorWithPatternImage來設(shè)置背景色;
  2. 在view中添加一個UIImageView作為一個子View嫁佳。

如果你使用全畫幅的背景圖挨队,你就必須使用UIImageView因為UIColor的colorWithPatternImage是用來創(chuàng)建小的重復(fù)的圖片作為背景的。這種情形下使用UIImageView可以節(jié)約不少的內(nèi)存:

// You could also achieve the same result in Interface Builder
UIImageView *backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background"]];
[self.view addSubview:backgroundView];

如果你用小圖平鋪來創(chuàng)建背景蒿往,你就需要用UIColor的colorWithPatternImage來做了盛垦,它會更快地渲染也不會花費很多內(nèi)存:

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

18. 減少使用Web特性

UIWebView很有用,用它來展示網(wǎng)頁內(nèi)容或者創(chuàng)建UIKit很難做到的動畫效果是很簡單的一件事瓤漏。
但是你可能有注意到UIWebView并不像驅(qū)動Safari的那么快腾夯。這是由于以JIT compilation 為特色的Webkit的Nitro Engine的限制。
所以想要更高的性能你就要調(diào)整下你的HTML了蔬充。第一件要做的事就是盡可能移除不必要的javascript蝶俱,避免使用過大的框架。能只用原生js就更好了饥漫。
另外榨呆,盡可能異步加載例如用戶行為統(tǒng)計script這種不影響頁面表達的javascript。
最后庸队,永遠要注意你使用的圖片愕提,保證圖片的符合你使用的大小馒稍。使用Sprite sheet提高加載速度和節(jié)約內(nèi)存。
更多相關(guān)信息可以看下 WWDC 2012 session #601 – Optimizing Web Content in UIWebViews and Websites on iOS

19. 設(shè)定Shadow Path

如何在一個View或者一個layer上加一個shadow呢浅侨,QuartzCore框架是很多開發(fā)者的選擇:

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

看起來很簡單纽谒,對吧。
可是如输,壞消息是使用這個方法也有它的問題… Core Animation不得不先在后臺得出你的圖形并加好陰影然后才渲染鼓黔,這開銷是很大的。
使用shadowPath的話就避免了這個問題:
view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath];
使用shadow path的話iOS就不必每次都計算如何渲染不见,它使用一個預(yù)先計算好的路徑澳化。但問題是自己計算path的話可能在某些View中比較困難,且每當view的frame變化的時候你都需要去update shadow path.
想了解更多可以看看Mark Pospesel的這篇稳吮。

20. 優(yōu)化Table View

Table view需要有很好的滾動性能缎谷,不然用戶會在滾動過程中發(fā)現(xiàn)動畫的瑕疵。
為了保證table view平滑滾動灶似,確保你采取了以下的措施:

  • 正確使用reuseIdentifier來重用cells
  • 盡量使所有的view opaque列林,包括cell自身
  • 避免漸變,圖片縮放酪惭,后臺選人
  • 緩存行高
  • 如果cell內(nèi)現(xiàn)實的內(nèi)容來自web希痴,使用異步加載,緩存請求結(jié)果
  • 使用shadowPath來畫陰影
  • 減少subviews的數(shù)量
  • 盡量不適用cellForRowAtIndexPath:春感,如果你需要用到它砌创,只用一次然后緩存結(jié)果
  • 使用正確的數(shù)據(jù)結(jié)構(gòu)來存儲數(shù)據(jù)
  • 盡量使用rowHeight, sectionFooterHeightsectionHeaderHeight來設(shè)定固定的高,不要請求delegate

21. 選擇正確的數(shù)據(jù)存儲選項

當做本地數(shù)據(jù)存儲時你會怎么做鲫懒?
你有很多選擇嫩实,比如:

  • 使用NSUerDefaults
  • 使用XML, JSON, 或者 plist
  • 使用NSCoding存檔
  • 使用類似SQLite的本地SQL數(shù)據(jù)庫
  • 使用 Core Data

NSUserDefaults的問題是什么?雖然它很nice也很便捷窥岩,但是它只適用于小數(shù)據(jù)甲献,比如一些簡單的布爾型的設(shè)置選項,再大點你就要考慮其它方式了

XML這種結(jié)構(gòu)化檔案呢谦秧?總體來說竟纳,你需要讀取整個文件到內(nèi)存里去解析,這樣是很不經(jīng)濟的疚鲤。使用SAX又是一個很麻煩的事情锥累。

NSCoding?不幸的是集歇,它也需要讀寫文件桶略,所以也有以上問題。

當存儲大塊數(shù)據(jù)時,以上的方法都不適用. 在這種應(yīng)用場景下,使用SQLite 或者 Core Data比較好际歼。使用這些技術(shù)你用特定的查詢語句就能只加載你需要的對象惶翻。
在性能層面來講,SQLite和Core Data是很相似的鹅心。他們的不同在于具體使用方法吕粗。Core Data代表一個對象的graph model,但SQLite就是一個DBMS旭愧。Apple在一般情況下建議使用Core Data颅筋,但是如果你有理由不使用它,那么就去使用更加底層的SQLite吧输枯。

22. 加速啟動時間

快速打開app是很重要的议泵,特別是用戶第一次打開它時,對app來講桃熄,第一印象太太太重要了先口。

  • 你能做的就是使它盡可能做更多的異步任務(wù),比如加載遠端或者數(shù)據(jù)庫數(shù)據(jù)瞳收,解析數(shù)據(jù)碉京。
    還是那句話,避免過于龐大的XIB缎讼,因為他們是在主線程上加載的收夸。所以盡量使用沒有這個問題的Storyboards吧坑匠!
    注意血崭,用Xcode debug時watchdog并不運行,一定要把設(shè)備從Xcode斷開來測試啟動速度

23. 使用Autorelease Pool

NSAutoreleasePool負責釋放block中的autoreleased objects厘灼。一般情況下它會自動被UIKit調(diào)用夹纫。但是有些狀況下你也需要手動去創(chuàng)建它。
假如你創(chuàng)建很多臨時對象设凹,你會發(fā)現(xiàn)內(nèi)存一直在減少直到這些對象被release的時候舰讹。這是因為只有當UIKit用光了autorelease pool的時候memory才會被釋放。
好消息是你可以在你自己的@autoreleasepool里創(chuàng)建臨時的對象來避免這個行為:

    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對象
更多關(guān)于NSAutoreleasePool請參考官方文檔闪朱。

24. 選擇是否緩存圖片

常見的從bundle中加載圖片的方式有兩種月匣,一個是用imageNamed,二是用imageWithContentsOfFile奋姿,第一種比較常見一點锄开。

既然有兩種類似的方法來實現(xiàn)相同的目的,那么他們之間的差別是什么呢称诗?

imageNamed的優(yōu)點是當加載時會緩存圖片萍悴。imageNamed的文檔中這么說:
這個方法用一個指定的名字在系統(tǒng)緩存中查找并返回一個圖片對象如果它存在的話。如果緩存中沒有找到相應(yīng)的圖片,這個方法從指定的文檔中加載然后緩存并返回這個對象癣诱。
相反的计维,imageWithContentsOfFile僅加載圖片。
下面的代碼說明了這兩種方法的用法:

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

那么我們應(yīng)該如何選擇呢撕予?
如果你要加載一個大圖片而且是一次性使用鲫惶,那么就沒必要緩存這個圖片,用imageWithContentsOfFile足矣实抡,這樣不會浪費內(nèi)存來緩存它剑按。
然而,在圖片反復(fù)重用的情況下imageNamed是一個好得多的選擇澜术。

25. 避免日期格式轉(zhuǎn)換

如果你要用NSDateFormatter來處理很多日期格式艺蝴,應(yīng)該小心以待。就像先前提到的鸟废,任何時候重用NSDateFormatters都是一個好的實踐猜敢。
然而,如果你需要更多速度盒延,那么直接用C是一個好的方案缩擂。Sam Soffes有一個不錯的帖子([http://soff.es/how-to-drastically-improve-your-app-with-an-afternoon-and-instruments)里面有一些可以用來解析ISO-8601日期字符串的代碼,簡單重寫一下就可以拿來用了添寺。
嗯胯盯,直接用C來搞,看起來不錯了计露,但是你相信嗎博脑,我們還有更好的方案!
如果你可以控制你所處理的日期格式,盡量選擇Unix時間戳。你可以方便地從時間戳轉(zhuǎn)換到NSDate:

    - (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {
 return[NSDate dateWithTimeIntervalSince1970:timestamp];
 }

這樣會比用C來解析日期字符串還快鸿吆!
需要注意的是食茎,許多web API會以微秒的形式返回時間戳,因為這種格式在javascript中更方便使用。記住用dateFromUnixTimestamp之前除以1000就好了。

一、首頁啟動速度

啟動過程中做的事情越少越好(盡可能將多個接口合并)

不在UI線程上作耗時的操作(數(shù)據(jù)的處理在子線程進行烟具,處理完通知主線程刷新節(jié)目)

在合適的時機開始后臺任務(wù)(例如在用戶指引節(jié)目就可以開始準備加載的數(shù)據(jù))

盡量減小包的大小

優(yōu)化方法:

量化啟動時間

啟動速度模塊化

輔助工具(友盟,聽云奠蹬,F(xiàn)lurry)

二朝聋、頁面瀏覽速度

json的處理(iOS 自帶的NSJSONSerialization,Jsonkit罩润,SBJson)

數(shù)據(jù)的分頁(后端數(shù)據(jù)多的話玖翅,就要分頁返回翼馆,例如網(wǎng)易新聞,或者 微博記錄)

數(shù)據(jù)壓縮(大數(shù)據(jù)也可以壓縮返回金度,減少流量应媚,加快反應(yīng)速度)

內(nèi)容緩存(例如網(wǎng)易新聞的最新新聞列表都是要緩存到本地,從本地加載猜极,可以緩存到內(nèi)存中姜,或者數(shù)據(jù)庫,根據(jù)情況而定)

延時加載tab(比如app有5個tab跟伏,可以先加載第一個要顯示的tab丢胚,其他的在顯示時候加載,按需加載)

算法的優(yōu)化(核心算法的優(yōu)化受扳,例如有些app 有個 聯(lián)系人姓名用漢語拼音的首字母排序)

三携龟、操作流暢度優(yōu)化:

Tableview 優(yōu)化(tableview cell的加載優(yōu)化)

ViewController加載優(yōu)化(不同view之間的跳轉(zhuǎn),可以提前準備好數(shù)據(jù))

四勘高、數(shù)據(jù)庫的優(yōu)化:

數(shù)據(jù)庫設(shè)計上面的重構(gòu)

查詢語句的優(yōu)化

分庫分表(數(shù)據(jù)太多的時候峡蟋,可以分不同的表或者庫)

五、服務(wù)器端和客戶端的交互優(yōu)化:

客戶端盡量減少請求

服務(wù)端盡量做多的邏輯處理

服務(wù)器端和客戶端采取推拉結(jié)合的方式(可以利用一些同步機制)

通信協(xié)議的優(yōu)化华望。(減少報文的大腥锘取)

電量使用優(yōu)化(盡量不要使用后臺運行)

六、非技術(shù)性能優(yōu)化

產(chǎn)品設(shè)計的邏輯性(產(chǎn)品的設(shè)計一定要符合邏輯赖舟,或者邏輯盡量簡單蓬戚,否則會讓程序員抓狂,有時候用了好大力氣宾抓,才可以完成一個小小的邏輯設(shè)計問題)

界面交互的規(guī)范(每個模塊的界面的交互盡量統(tǒng)一子漩,符合操作習慣)

代碼規(guī)范(這個可以隱形帶來app 性能的提高,比如 用if else 還是switch 洞慎,或者是用痛单!還是 ==)

code review(堅持code Review 持續(xù)重構(gòu)代碼嘿棘。減少代碼的邏輯復(fù)雜度)

日常交流(經(jīng)常分享一些代碼劲腿,或者邏輯處理中的坑)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鸟妙,隨后出現(xiàn)的幾起案子焦人,更是在濱河造成了極大的恐慌,老刑警劉巖重父,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件花椭,死亡現(xiàn)場離奇詭異,居然都是意外死亡房午,警方通過查閱死者的電腦和手機矿辽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人袋倔,你說我怎么就攤上這事雕蔽。” “怎么了宾娜?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵批狐,是天一觀的道長。 經(jīng)常有香客問我前塔,道長嚣艇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任华弓,我火速辦了婚禮食零,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寂屏。我一直安慰自己慌洪,他們只是感情好,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布凑保。 她就那樣靜靜地躺著冈爹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪欧引。 梳的紋絲不亂的頭發(fā)上频伤,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音芝此,去河邊找鬼憋肖。 笑死,一個胖子當著我的面吹牛婚苹,可吹牛的內(nèi)容都是我干的岸更。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼膊升,長吁一口氣:“原來是場噩夢啊……” “哼怎炊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起廓译,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤评肆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后非区,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓜挽,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年征绸,在試婚紗的時候發(fā)現(xiàn)自己被綠了久橙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俄占。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖淆衷,靈堂內(nèi)的尸體忽然破棺而出颠放,到底是詐尸還是另有隱情,我是刑警寧澤吭敢,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布碰凶,位于F島的核電站,受9級特大地震影響鹿驼,放射性物質(zhì)發(fā)生泄漏欲低。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一畜晰、第九天 我趴在偏房一處隱蔽的房頂上張望砾莱。 院中可真熱鬧,春花似錦凄鼻、人聲如沸腊瑟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽闰非。三九已至,卻和暖如春峭范,著一層夾襖步出監(jiān)牢的瞬間财松,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工纱控, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留辆毡,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓甜害,卻偏偏與公主長得像舶掖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子尔店,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 性能對 iOS 應(yīng)用的開發(fā)尤其重要眨攘,如果你的應(yīng)用失去反應(yīng)或者很慢,那么App Store的評論顯而易見闹获。然而...
    楚簡約閱讀 2,373評論 0 6
  • iPhone上面的應(yīng)用一直都是以流暢的操作體驗而著稱期犬,但是由于之前開發(fā)人員把注意力更多的放在開發(fā)功能上面,比較少去...
    樹下老男孩閱讀 16,501評論 15 118
  • 在開發(fā)iOS應(yīng)用程序時避诽,讓程序具有良好的性能是非常關(guān)鍵的。這也是用戶所期望的璃谨,如果你的程序運行遲鈍或緩慢沙庐,會招致用...
    imkakaxi閱讀 1,003評論 0 0
  • iPhone上面的應(yīng)用一直都是以流暢的操作體驗而著稱鲤妥,但是由于之前開發(fā)人員把注意力更多的放在開發(fā)功能上面,比較少去...
    你飛躍俊杰閱讀 451評論 0 0
  • 轉(zhuǎn)載:http://www.reibang.com/p/2a01e5e2141f http://blog.csdn...
    F麥子閱讀 418評論 0 2