性能優化
所謂的性能優化一般都是在運行和內存之間切換,平衡兩者的負荷。具體的性能到底好不好,一定要通過實際測試才來決定到底用不用采取下面的幾種優化方案之一。試想,如果表格刷新幀率已經非常接近60了,但是還想進一步采取緩存行高來優化項目。很肯定的說,這種做法是不明智的,因為刷新幀率已經非常接近60了,即使再怎么優化也不可能突破60這個數值,反而采取緩存行高的方式會消耗內存,對整個項目的優化百害而無一利。
CPU、GPU概念
關于繪圖和動畫有兩種處理方式CPU(中央處理器)和GPU(圖形處理器),CPU的工作都在軟件層面,而GPU的在硬件層面。
總的來說,可以使用CPU做任何事情,但是對于圖像的處理,通常GPU會更快,所以,我們想盡可能的把屏幕渲染的工作交給硬件去處理,而問題在于GPU并沒有無限制處理的性能,一旦資源用盡,即使CPU并沒有完全占用,GPU性能還是會下降。所以,目前大多的性能優化都是關于智能利用GPU和CPU,平衡它們之間工作負載。
離屏渲染
Core Animation可以用來檢測應用內的FPS,當FPS 低于45的時候,用戶就會察覺到到滑動有卡頓。理論上60最佳,實際過程中59就可以了。
- 離屏渲染:
是指在屏幕外部不可見部分或異步操作,準備好圖片。
優點:提前畫好圖片。
缺點:會在CPU和GPU之間頻繁切換,一會計算坐標,一會畫圖。會導致CPU消耗高些,但顯示性能稍微好些。一般會用shadowPath和shouldRasterise(柵格化)來優化。
針對混合圖層和圖片拉升效果引起的性能問題,一般可以通過繪制指定尺寸大小、不透明的圖片來優化性能。如下是兩個代碼片段分別針對這一點繪制指定尺寸和圓角圖片
- 繪制圓角圖片
- (UIImage *)imageWithSize:(CGSize )size backColor:(UIColor *)backColor{
CGRect rect = CGRectMake(0, 0, size.width, size.height);
//1.上下文 ->在內存中開辟一個地址,這和屏幕顯示無關
/*
參數:
1.繪圖尺寸
2.是否不透明:false(透明)/true(不透明),
第二個參數,一般設置為true更好些,會避免圖層重疊導致的混合渲染
3.屏幕的分辨率,
默認如果不指定,默認的圖像顯示1.0的分辨率,圖像質量不好,
可以指定為0,會選擇當前屏幕的分辨率
*/
UIGraphicsBeginImageContextWithOptions(size, YES, 0);
//2.繪圖 drawInRect就是在指定區域內拉伸屏幕 核心繪圖的重點:路徑
//設置背景顏色
[backColor setFill];
UIRectFill(rect);
[self drawInRect:rect];
//3.取得結果
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//4.關閉上下文
UIGraphicsEndImageContext();
return image;
}
- 繪制指定尺寸大小的圖片
- (UIImage *)circleImageWithSize:(CGSize )size
backColor:(UIColor *)backColor
lineColor:(UIColor *)lineColor
lineWidth:(CGFloat)linewidth
{
CGRect rect = CGRectMake(0, 0, size.width, size.height);
//1.上下文 ->在內存中開辟一個地址,這和屏幕顯示無關
/*
參數:
1.繪圖尺寸
2.是否不透明:false(透明)/true(不透明),
第二個參數,一般設置為true更好些,會避免圖層重疊導致的混合渲染
3.屏幕的分辨率,
默認如果不指定,默認的圖像顯示1.0的分辨率,圖像質量不好,
可以指定為0,會選擇當前屏幕的分辨率
*/
UIGraphicsBeginImageContextWithOptions(size, YES, 0);
//2.繪圖 drawInRect就是在指定區域內拉伸屏幕 核心繪圖的重點:路徑
//a.背景填充顏色
[backColor setFill];
UIRectFill(rect);
//b.實例化一個圓形的路徑
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
//c.進行路徑裁剪 --后續的繪圖,都會在圓形路徑內部,外部的全部干掉
[path addClip];
//d.繪圖
[self drawInRect:rect];
//f.繪制邊線
[lineColor setStroke];
path.lineWidth = linewidth;
[path stroke];
//3.取得結果
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//4.關閉上下文
UIGraphicsEndImageContext();
return image;
}
TableViewCell的優化
- tableViewCell復用機制
- 緩存行高
- 設計統一規格的cell,擅用hidden來顯示/隱藏subviews,而不是動態的通過addsubview去控制。
- UITableViewCell上的子View的opaque屬性設為yes
- 使用局部刷新
- 減少subviews的數量
- 使用rowHeight, sectionFooterHeight 和 sectionHeaderHeight來設定固定的高,不要請求delegate
- 使用正確的數據結構來存儲數據
- 所有的子視圖都要制定背景顏色(如UIViewController中不設置背景顏色,會出現卡頓現象)。
- 所有的顏色都不要使用alpha,一旦涉及alpha就會涉及多個圖層渲染的計算,運算量會很大,對資源的消耗會非常巨大。
- 柵格化:將cell中的所有內容,生成一張獨立的圖像。只要在自定義cell類中的初始化發放中寫下如下兩行代碼:
self.layer.shadowPath = YES;
self.layer.rasterizationScale = [UIScreen mainScreen].scale;
- 異步繪制。如果cell布局比較復雜建議使用異步繪制,其他情況下不建議使用。同樣也是在自定義cell類中的初始化中完成,只要寫上一句代碼即可。
self.layer.drawsAsynchronously = YES;
圖片加載
- 本地圖片加載方式:
1、[UIImage imageNamed:@""]
優點:當加載時會緩存圖片,該方法用一個指定的名字在系統緩存中查找并返回一個圖片對象如果它存在的話。如果緩存中沒有找到相應的圖片,這個方法從指定的文檔中加載然后緩存并返回這個對象。
2、[[UIImage alloc] initWithContentsOfFile:@""]
僅加載圖片。
3、該如何選擇:
如果你要加載一個大圖片而且是一次性使用,那么就沒必要緩存這個圖片,用imageWithContentsOfFile足矣,這樣不會浪費內存來緩存它。
然而,在圖片反復重用的情況下imageNamed是一個好得多的選擇。
編碼過程
- 延遲加載Views
更多的view意味著更多的渲染,即更多的CPU和內存消耗。想象一下一個用戶點擊一個按鈕的時候需要呈現一個view的場景。有兩種實現方法:
1、創建并隱藏這個view當這個screen加載的時候,當需要時顯示它;
2、當需要時才創建并展示。
每個方案都有其優缺點:
用第一種方案的話因為你需要一開始就創建一個view并保持它直到不再使用,這就會更加消耗內存。然而這也會使你的app操作更敏感因為當用戶點擊按鈕的時候它只需要改變一下這個view的可見性。
第二種方案則相反-消耗更少內存,但是會在點擊按鈕的時候比第一種稍顯卡頓。
- 重用大開銷對象
一些objects的初始化很慢,比如NSDateFormatter和NSCalendar??梢酝ㄟ^添加屬性到你的class里或者創建靜態變量(對象會在你的app運行時一直存在于內存中,和單例(singleton)很相似)來實現。
- 選擇正確的數據格式,避免反復處理數據
- 選擇正確的數據存儲選項
數據存儲大致分plist、對象歸檔、sqlite。NSUserDefault適合存儲小量數據,私密信息使用Keychain, sqlite適合大量數據,歸檔性能較低,盡量避免使用。
處理內存警告
UIKit提供了幾種收集低內存警告的方法:
1、在app delegate
中使用applicationDidReceiveMemoryWarning:
的方法
2、在你的自定義UIViewController
的子類(subclass)中覆蓋didReceiveMemoryWarning
3、注冊并接收 UIApplicationDidReceiveMemoryWarningNotification
的通知
代碼優化
- 良好的編碼習慣,遵循代碼規范、mvc架構、利用第三方AFN時,做一層網絡隔離,減少對第三方的依賴