性能優化是一個大的問題,所以首先是需要把這個問題分而化之,把它分解成一個個影響app性能的小問題才能進行回答,所以在這里做出一些整理來回答這個問題,同時也提醒自己再次遇到如此大的問題時學會分析問題的本身以及從哪些方面去回答這些問題。
影響app性能的幾個問題有:
1. 網絡性能
網絡性能優化涉及到DNS解析,路由算法,以及服務器端性能,不是很了解,可參看一下文章:
攜程App的網絡性能優化實踐
影響移動應用網絡性能的三大因素
2. 內存問題
在MRC時代,手動釋放內存會導致大量內存的泄露。但是在ARC時代解決了大部分的內存泄露,但是仍然會出現內存泄露的問題:
1. 循環引用
2. Core Animation對象手動釋放
3. UIWebView內存泄露
一些詳細介紹如下:
ARC下的內存泄漏
ARC 下內存泄露的那些點
3. 主線程阻塞
所有的用戶輸入和UIKit的渲染是在主線程執行。所以要保證app的流暢度就一定不能阻塞主線程,把可以在子線程中做的事放到子線程中來減少主線程的計算與處理。
假如在主線程中執行如下操作:
1. 網絡同步請求
2. I/O操作
3. 大量運算
4. 解壓縮
...
因為需要處理的多所以會阻塞主線程,導致卡頓,因此要減少主線程中耗時的操作,使用多線程(NSThread、NSOperationQueue, GCD)來處理這些。可以查看OS X 和 iOS 中的多線程技術關于多線程的介紹。還有主線程關于渲染的處理會影響效率,所以這一塊也是需要處理的。
4. Offscreen rendering(離屏渲染)
離屏渲染,指的是GPU在當前屏幕緩沖區以外新開辟一個緩沖區進行渲染操作。離屏渲染意味著你App的部分區域每一幀渲染了兩次。所以會造成一定的性能損失。
對于UIView或者CALayer的frame,bounds,transform等屬性的改變,消耗的資源遠大于他們其他的屬性改變。
可以參考以下文章:
繪制像素到屏幕上
繪制陰影引發的 iOS 繪圖性能問題總結
iOS 離屏渲染的研究
5. 圖片的處理
通常會用imageNamed:來加載mainbundle中的圖片,此函數會緩存加載的image。因此,對于那些被重用的圖片,這個API很高效。但是對于那些使用很少的圖片,用這個就很耗內存。
所以在加載使用一次的應用圖片時使用initWithContentsOfFile:函數,而在加載多次使用的圖片時就使用imageNamed:函數。例如加載引導頁的圖片時使用載入路徑的方式,而使用通用的背景圖的就使用imageNamed:的方式。
//使用路徑方式載入圖片
NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:fileType];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:path];
//使用圖片名的方式載入圖片
UIImage *image = [UIImage imageNamed:fileName];
或
//讀取本地圖片的 和imageNamed一樣,但是性能比后者要強很多,兩個參數,前面一個是 文件名,后面一個是類型
#define LoadImage(_pointer) [UIImage imageNamed:[UIUtil imageName:_pointer]] //可以用來直接傳圖片名字
#define LoadImageWithType(file,ext) [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:file ofType:ext]]
一般的優化技術就是在減少內存使用,減少主線程業務處理,用空間來換時間等等,基于這些策略及技術考慮來選擇優化方向。
以下是iOS的一些細節優化策略
- 避免對UIView使用透明。(UIView默認是非透明)。原因是透明對性能要求較高,如果在滾動時頁面比較復雜,體驗上的差異會相對明顯。
- 避免過于龐大的xib。(如果不得不使用一個ViewController作為xib,也應該將其其中的子視圖拆成小的xib)。
需要注意的是,當你加載一個XIB的時候所有內容都被放在了內存里,包括任何圖片。如果有一個不會即刻用到的view,你這就是在浪費寶貴的內存資源了。Storyboards就是另一碼事兒了,storyboard僅在需要時實例化一個view controller.
不要阻塞主線程。
- 使圖片符合UIImageView的尺寸。不要在運行的時候再讓UIImageView自行壓縮,因為這樣會降低運行時的性能。(注:手動壓縮圖片的方法,在context中使用drawInRect)
- 選擇合適的collection,數據結構決定了算法的效率。 如:Array使用下標查找較快,但插入和刪除較慢。set進行插入和刪除很快。
- 使用緩存,因為數據具有時效的,所以對于時效性要求不高的數據完全可以使用緩存來保證快速顯示。例如URL對應的圖片緩存(SDWebImage),通過數據庫活core Data來保存不需要變動的數據,UIWeb的緩存等都屬于這種。
- 處理低內存警告。在收到內存警告時,清除對cache的強引用,沒有當前顯示需要的image,以及一些其他可以再創建的對象。
- 重用一些高消耗的對象,如NSDateFormatter、NSCalender等。解決方法:可以將其作為property、甚至是靜態變量作為單例在APP中使用。并且,NSDateFormatter的 setDateFormate也是非常消耗資源的一個操作。
- 網絡傳輸過來的數據,往往是json或xml字符串。直接將這些字符串轉換成我們需要的數據結構(自定義類或者NSDictionary),避免后續使用的時候還要做數據結構轉換產生不必要的消耗。
- 設置UIView的背景圖片時,如果是整幅圖,就采用addSubView一個UIImageView;如果是要重復平鋪一個小圖,就使用colorWithPatternImage,因為這個函數的設計上就是針對小圖的,如果用于整幅大圖來做背景,反而會消耗更多內存。
- 在臨時創建大量對象時,使用NSAutoreleasepool,例如,一個循環用于創建包含多個對象的數組,在循環體內,即可使用@autoreleasepool包裹創建代碼。使用系統的@autoreleasepool會有延遲,內存不會馬上釋放。
- 對于排版復雜的文字或者圖文混排,使用CoreText技術。(而不是一味地堆UILabel)
- 在對渲染的效率要求較高的頁面中,避免使用UILabel、UITextView等在主線程中進行排版和繪制的控件。應自定義文本控件,用TextKit或者CoreText進行文本異步繪制。另外,還有facebook的AsyncDisplayKit框架可以采用。
將繪制圖像放在次線程中執行,如在次線程中使用 CGContext進行畫圖,在主線程中 layer.contents = img。
圖片和視圖的大小避免超過4096*4096,因為這是目前iphone5到iphone6p以及ipad僅僅通過GPU就直接處理的紋理尺寸上限,否則就GPU就會提交CPU先處理,這樣開銷很大。
- 減少視圖或者layer的層級數量,在有多個層級時,可以將多圖合并成一張圖,再渲染顯示。
- 電量消耗:減少耗電與流量的操作。GPS在獲取用戶位置之后,就進行關閉,因為它非常耗電。
- 關于后臺運行。進入后臺后,即盡量減少內存占用、釋放所有的共享資源(如Calender或address book),因為iOS會kill后臺中內存消耗最多的或者進入后臺還占用共享資源的進程。
參考文章:
iOS App性能優化
讓App的運行速度與響應速度趨于一流
程序猿進化必讀:讓App的運行速度與響應速度趨于一流(iOS)
iOS應用性能調優的4個建議和技巧