25條提高iOS App性能的技巧和訣竅

来源:http://www.cnblogs.com/YangFuShun/archive/2017/12/11/8022097.html
-Advertisement-
Play Games

25條提高iOS App性能的技巧和訣竅 當我們開發iOS應用時,好的性能對我們的App來說是很重要的。你的用戶也希望如此,但是如果你的app表現的反應遲鈍或者很慢也會傷害到你的審核。 然而,由於IOS設備的限制有時很難工作得很正確。我們開發時有很多需要我們記住這些容易忘記的決定對性能的影響。 這是 ...


25條提高iOS App性能的技巧和訣竅

當我們開發iOS應用時,好的性能對我們的App來說是很重要的。你的用戶也希望如此,但是如果你的app表現的反應遲鈍或者很慢也會傷害到你的審核。 

     然而,由於IOS設備的限制有時很難工作得很正確。我們開發時有很多需要我們記住這些容易忘記的決定對性能的影響。 

     這是為什麼我寫這篇文章的原因。這篇文章用備忘錄的形式集合了25個技巧和訣竅可以用來提高你的app性能。所以保持閱讀來給你未來的App一個很不錯的提高。 

      Note:在優化代碼之前,必須保證有個需要解決的問題!不要陷入"pre-optimizing(預優化)"你的代碼。勤 用Instruments分析你的代碼,發現任何一個需要提高的地方。Matt Galloway寫了一個使用Instruments優化代碼的的教程

    

    以下這些技巧分為三個不同那個的級別---基礎,中級,高級。 

   基礎

   這些技巧你要總是想著實現在你開發的App中。 

   1. 用ARC去管理記憶體(Use ARC to Manage Memory)

   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的常用代碼

 

[cpp] view plain copy  
  1. UIView *view =[[UIView alloc] init];  
  2. //...  
  3. [self.view addSubview:view];  
  4. [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的時候調用這個方 

[cpp] view plain copy  
  1. NSString *CellIdentifier = @"Cell";  
  2.   
  3. 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中的這個方法: 

[cpp] view plain copy  
  1. + (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler  

     或者使用第三方框架比如 AFNetworking

    如果你在做任何大開銷的操作(比如執行一個耗時的計算,或者讀寫磁碟)使用Grand Central Dispatch(GCD)或者 NSOperations 和 NSOperationQueues. 

    使用GCD的模板如下代碼所示: 

[cpp] view plain copy  
  1. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
  2.   
  3.     // switch to a background thread and perform your expensive operation  
  4.   
  5.    
  6.   
  7.     dispatch_async(dispatch_get_main_queue(), ^{  
  8.   
  9.         // switch back to the main thread to update your UI  
  10.   
  11.    
  12.   
  13.     });  
  14.   
  15. });  

      這裡為什麼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。 

[cpp] view plain copy  
  1. + (NSMutableURLRequest *)imageRequestWithURL:(NSURL *)url {  
  2.   
  3.    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];  
  4.   
  5.   
  6.    request.cachePolicy = NSURLRequestReturnCacheDataElseLoad; // this will make sure the request always returns the cached image  
  7.   
  8.     request.HTTPShouldHandleCookies = NO;  
  9.   
  10.     request.HTTPShouldUsePipelining = YES;  
  11.   
  12.     [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];  
  13.   
  14.     return request;  
  15.   
  16. }  

      如果想知道更多關於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載入,第一次被調用然後創建它,之後就使用已創建在的實例 

[cpp] view plain copy  
  1. // in your .h or inside a class extension  
  2.   
  3. @property (nonatomic, strong) NSDateFormatter *formatter;  
  4.   
  5.    
  6. // inside the implementation (.m)  
  7.   
  8. // When you need, just use self.formatter  
  9.   
  10. - (NSDateFormatter *)formatter {  
  11.   
  12.     if (! _formatter) {  
  13.   
  14.         _formatter = [[NSDateFormatter alloc] init];  
  15.   
  16.         _formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; // twitter date format  
  17.   
  18.     }  
  19.   
  20.     return _formatter;  
  21.   
  22. }  

       同樣要記住設置一個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方式會節約很多記憶體。 

[cpp] view plain copy  
  1. // You could also achieve the same result in Interface Builder  
  2.   
  3.  UIImageView *backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background"]];  
  4.   
  5. [self.view addSubview:backgroundView];  

     儘管如此,如果你計劃用模式圖片背景,你應該是用UIColor的colorWithPatternImage。它更快一些,而且這種情況不會使用很多記憶體。 

[cpp] view plain copy  
  1. 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框架到工程中,然後寫如下代碼: 

[cpp] view plain copy  
  1. #import <QuartzCore/QuartzCore.h>  
  2.   
  3.  // Somewhere later ...  
  4.   
  5. UIView *view = [[UIView alloc] init];  
  6.   
  7.  // Setup the shadow ...  
  8.   
  9. view.layer.shadowOffset = CGSizeMake(-1.0f, 1.0f);  
  10.   
  11. view.layer.shadowRadius = 5.0f;  
  12.   
  13. view.layer.shadowOpacity = 0.6;  

      看起來非常簡單,是吧? 

      不好的是這個方法有一個問題。核心動畫必須要先做一幕動畫確定視圖具體形狀之後才渲染陰影,這是非常費事的操作。 

      這裡有個替代方法讓系統更好的渲染,設置陰影路徑: 

[cpp] view plain copy  
  1. 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段中創建臨時對象來避免上述情況。代碼如下所示。

[cpp] view plain copy  
  1. NSArray *urls = <# An array of file URLs #>;  
  2. for (NSURL *url in urls) {  
  3.   
  4.     @autoreleasepool {  
  5.   
  6.         NSError *error;  
  7.   
  8.         NSString *fileContents = [NSString stringWithContentsOfURL:url  
  9.   
  10.                                          encoding:NSUTF8StringEncoding error:&error];  
  11.   
  12.         /* Process the string, creating and autoreleasing more objects. */  
  13.   
  14.     }  
  15.   
  16. }  

    在每次迭代之後會自動釋放所有的對象。 

    

    你可以閱讀更多關於NSAutoreleasePool的內容Apple’s official documentation.

 

24)緩存圖像

     這裡有兩種方法去載入app束中的Image,第一個常見的方式是用imageNamed. 第二個是使用imageWithContentsOfFile 

     為什麼會有兩種方法,它們有效率嗎? 

 

     imageNamed 在載入時有緩存的優勢。文檔 documentation for imageNamed是這樣解釋的: 

     這個方法看起來在系統緩存一個圖像對象並指定名字,如果存在則返回對象,如果匹配圖像的對象不在緩存中,這個方法會從指定的文件中載入數據,並緩存它,然後返回結果對象。 

    作為替代,imageWithContendsOfFile 簡單的載入圖像並不會緩存。 

    這兩個方法的的演示片段如下: 

[cpp] view plain copy  
  1. UIImage *img = [UIImage imageNamed:@"myImage"]; // caching  
  2.   
  3. // or  
  4. 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,如下麵所示: 

[cpp] view plain copy  
  1. - (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {  
  2.   
  3.   return [NSDate dateWithTimeIntervalSince1970:timestamp];  
  4.   
  5. }  

     這甚至比C函數更快

     註意,很多WEB APIs返回時間戳是毫秒,因為這對於javascript最終來使用和處理數據是非常常見的。只要記住將這個時間戳除以1000再傳遞給dateFromUnixTimestamp方法即可。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • < > 尖括弧,用於分隔字元串,字元串為語法元素的名稱,SQL語言的非終結符。::= 定義操作符。用在生成規則中,分隔規則定義的元素和規則定義。 被定義的元素位於操作符的左邊,規則定義位於操作符的右邊。[ ] 方括弧表示規則中的可選元素。方括弧中的規則部分可以明確指定也可以省略。{ } 花括弧聚集規 ...
  • https://www.cnblogs.com/Joetao/articles/2250516.html 本質上沒區別。只是函數有如:只能返回一個變數的限制。而存儲過程可以返回多個。 而函數是可以嵌入在sql中使用的,可以在select中調用,而存儲過程不行。 執行的本質都一樣。 函數限制比較多,比 ...
  • ViewPager是v4支持庫中的一個控制項,相信幾乎所有接觸Android開發的人都對它不陌生。之所以還要在這裡翻舊賬,是因為我在最近的項目中有多個需求用到了它,覺得自己對它的認識不夠深刻。我計劃從最簡單的使用場景出發,記錄我到目前為止所對ViewPager的使用情況以及有關它的一些知識點。 這個系 ...
  • 首先自我介紹一下,本人鳥窩,現在就職於xx共用汽車,擔任主程,目前用的技術棧是.net core+angular。 今天我講的是關於ReactNative從零基礎開發,希望可以對入門的新手,起到一個指導作用。 目前學習React Native跨平臺開發的人員比較多,乾ReactNative開發的程式 ...
  • 這裡將為你詳細介紹占位符的使用,將其學以致用,可以達到簡化佈局文件,減少字元串資源量。 1、在資源文件中的使用。 打開資源文件中的strings.xml文件,進行編輯。如下圖所示: 圖 1.0 2、獲取字元串資源文件的使用說明。 方式一: 輸出的結果是:13.0得分:12.22 方式二: 輸出的結果 ...
  • 近日,Google在12月發佈的安卓系統安全公告中披露了一個名為“Janus”安卓漏洞(漏洞編號:CVE-2017-13156)。該漏洞可以讓攻擊者繞過安卓系統的signature scheme V1簽名機制,進而直接對App進行篡改。而且由於安卓系統的其他安全機制也是建立在簽名和校驗基礎之上,該漏 ...
  • xamarin上常用的崩潰分析工具有TestFlight,HockeyApp, Crashlytics等。TestFlight沒用過,Crashlytics註冊需要訪問Google,不好弄,HockeyApp走通了,步驟記錄如下: 1.配置HockeyApp 進入官網https://hockeyap ...
  • 在前面已經初步封裝了一個MVP的網路請求框架,那隻是個雛形,還有很多功能不完善,現在進一步進行封裝。添加了網路請求時的等待框,retrofit中添加了日誌列印攔截器,添加了token攔截器,並且對DataManager類進行了擴展,真正體現它的作用,並且對大量的重覆代碼做了一定封裝,減少代碼的冗餘。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...