性能優(yōu)化
所謂的性能優(yōu)化一般都是在運(yùn)行和內(nèi)存之間切換,平衡兩者的負(fù)荷。具體的性能到底好不好,一定要通過實(shí)際測(cè)試才來決定到底用不用采取下面的幾種優(yōu)化方案之一。試想,如果表格刷新幀率已經(jīng)非常接近60了,但是還想進(jìn)一步采取緩存行高來優(yōu)化項(xiàng)目。很肯定的說,這種做法是不明智的,因?yàn)樗⑿聨室呀?jīng)非常接近60了,即使再怎么優(yōu)化也不可能突破60這個(gè)數(shù)值,反而采取緩存行高的方式會(huì)消耗內(nèi)存,對(duì)整個(gè)項(xiàng)目的優(yōu)化百害而無一利。
CPU、GPU概念
關(guān)于繪圖和動(dòng)畫有兩種處理方式CPU(中央處理器)和GPU(圖形處理器),CPU的工作都在軟件層面,而GPU的在硬件層面。
總的來說,可以使用CPU做任何事情,但是對(duì)于圖像的處理,通常GPU會(huì)更快,所以,我們想盡可能的把屏幕渲染的工作交給硬件去處理,而問題在于GPU并沒有無限制處理的性能,一旦資源用盡,即使CPU并沒有完全占用,GPU性能還是會(huì)下降。所以,目前大多的性能優(yōu)化都是關(guān)于智能利用GPU和CPU,平衡它們之間工作負(fù)載。
離屏渲染
Core Animation可以用來檢測(cè)應(yīng)用內(nèi)的FPS,當(dāng)FPS 低于45的時(shí)候,用戶就會(huì)察覺到到滑動(dòng)有卡頓。理論上60最佳,實(shí)際過程中59就可以了。

- 離屏渲染:
是指在屏幕外部不可見部分或異步操作,準(zhǔn)備好圖片。
優(yōu)點(diǎn):提前畫好圖片。
缺點(diǎn):會(huì)在CPU和GPU之間頻繁切換,一會(huì)計(jì)算坐標(biāo),一會(huì)畫圖。會(huì)導(dǎo)致CPU消耗高些,但顯示性能稍微好些。一般會(huì)用shadowPath和shouldRasterise(柵格化)來優(yōu)化。
針對(duì)混合圖層和圖片拉升效果引起的性能問題,一般可以通過繪制指定尺寸大小、不透明的圖片來優(yōu)化性能。如下是兩個(gè)代碼片段分別針對(duì)這一點(diǎn)繪制指定尺寸和圓角圖片
- 繪制圓角圖片
- (UIImage *)imageWithSize:(CGSize )size backColor:(UIColor *)backColor{
CGRect rect = CGRectMake(0, 0, size.width, size.height);
//1.上下文 ->在內(nèi)存中開辟一個(gè)地址,這和屏幕顯示無關(guān)
/*
參數(shù):
1.繪圖尺寸
2.是否不透明:false(透明)/true(不透明),
第二個(gè)參數(shù),一般設(shè)置為true更好些,會(huì)避免圖層重疊導(dǎo)致的混合渲染
3.屏幕的分辨率,
默認(rèn)如果不指定,默認(rèn)的圖像顯示1.0的分辨率,圖像質(zhì)量不好,
可以指定為0,會(huì)選擇當(dāng)前屏幕的分辨率
*/
UIGraphicsBeginImageContextWithOptions(size, YES, 0);
//2.繪圖 drawInRect就是在指定區(qū)域內(nèi)拉伸屏幕 核心繪圖的重點(diǎn):路徑
//設(shè)置背景顏色
[backColor setFill];
UIRectFill(rect);
[self drawInRect:rect];
//3.取得結(jié)果
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//4.關(guān)閉上下文
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.上下文 ->在內(nèi)存中開辟一個(gè)地址,這和屏幕顯示無關(guān)
/*
參數(shù):
1.繪圖尺寸
2.是否不透明:false(透明)/true(不透明),
第二個(gè)參數(shù),一般設(shè)置為true更好些,會(huì)避免圖層重疊導(dǎo)致的混合渲染
3.屏幕的分辨率,
默認(rèn)如果不指定,默認(rèn)的圖像顯示1.0的分辨率,圖像質(zhì)量不好,
可以指定為0,會(huì)選擇當(dāng)前屏幕的分辨率
*/
UIGraphicsBeginImageContextWithOptions(size, YES, 0);
//2.繪圖 drawInRect就是在指定區(qū)域內(nèi)拉伸屏幕 核心繪圖的重點(diǎn):路徑
//a.背景填充顏色
[backColor setFill];
UIRectFill(rect);
//b.實(shí)例化一個(gè)圓形的路徑
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
//c.進(jìn)行路徑裁剪 --后續(xù)的繪圖,都會(huì)在圓形路徑內(nèi)部,外部的全部干掉
[path addClip];
//d.繪圖
[self drawInRect:rect];
//f.繪制邊線
[lineColor setStroke];
path.lineWidth = linewidth;
[path stroke];
//3.取得結(jié)果
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//4.關(guān)閉上下文
UIGraphicsEndImageContext();
return image;
}
TableViewCell的優(yōu)化
- tableViewCell復(fù)用機(jī)制
- 緩存行高
- 設(shè)計(jì)統(tǒng)一規(guī)格的cell,擅用hidden來顯示/隱藏subviews,而不是動(dòng)態(tài)的通過addsubview去控制。
- UITableViewCell上的子View的opaque屬性設(shè)為yes
- 使用局部刷新
- 減少subviews的數(shù)量
- 使用rowHeight, sectionFooterHeight 和 sectionHeaderHeight來設(shè)定固定的高,不要請(qǐng)求delegate
- 使用正確的數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)數(shù)據(jù)
- 所有的子視圖都要制定背景顏色(如UIViewController中不設(shè)置背景顏色,會(huì)出現(xiàn)卡頓現(xiàn)象)。
- 所有的顏色都不要使用alpha,一旦涉及alpha就會(huì)涉及多個(gè)圖層渲染的計(jì)算,運(yùn)算量會(huì)很大,對(duì)資源的消耗會(huì)非常巨大。
- 柵格化:將cell中的所有內(nèi)容,生成一張獨(dú)立的圖像。只要在自定義cell類中的初始化發(fā)放中寫下如下兩行代碼:
self.layer.shadowPath = YES;
self.layer.rasterizationScale = [UIScreen mainScreen].scale;
- 異步繪制。如果cell布局比較復(fù)雜建議使用異步繪制,其他情況下不建議使用。同樣也是在自定義cell類中的初始化中完成,只要寫上一句代碼即可。
self.layer.drawsAsynchronously = YES;
圖片加載
- 本地圖片加載方式:
1、[UIImage imageNamed:@""]優(yōu)點(diǎn):當(dāng)加載時(shí)會(huì)緩存圖片,該方法用一個(gè)指定的名字在系統(tǒng)緩存中查找并返回一個(gè)圖片對(duì)象如果它存在的話。如果緩存中沒有找到相應(yīng)的圖片,這個(gè)方法從指定的文檔中加載然后緩存并返回這個(gè)對(duì)象。
2、[[UIImage alloc] initWithContentsOfFile:@""]僅加載圖片。
3、該如何選擇:
如果你要加載一個(gè)大圖片而且是一次性使用,那么就沒必要緩存這個(gè)圖片,用imageWithContentsOfFile足矣,這樣不會(huì)浪費(fèi)內(nèi)存來緩存它。
然而,在圖片反復(fù)重用的情況下imageNamed是一個(gè)好得多的選擇。
編碼過程
- 延遲加載Views
更多的view意味著更多的渲染,即更多的CPU和內(nèi)存消耗。想象一下一個(gè)用戶點(diǎn)擊一個(gè)按鈕的時(shí)候需要呈現(xiàn)一個(gè)view的場景。有兩種實(shí)現(xiàn)方法:
1、創(chuàng)建并隱藏這個(gè)view當(dāng)這個(gè)screen加載的時(shí)候,當(dāng)需要時(shí)顯示它;
2、當(dāng)需要時(shí)才創(chuàng)建并展示。
每個(gè)方案都有其優(yōu)缺點(diǎn):
用第一種方案的話因?yàn)槟阈枰婚_始就創(chuàng)建一個(gè)view并保持它直到不再使用,這就會(huì)更加消耗內(nèi)存。然而這也會(huì)使你的app操作更敏感因?yàn)楫?dāng)用戶點(diǎn)擊按鈕的時(shí)候它只需要改變一下這個(gè)view的可見性。
第二種方案則相反-消耗更少內(nèi)存,但是會(huì)在點(diǎn)擊按鈕的時(shí)候比第一種稍顯卡頓。
- 重用大開銷對(duì)象
一些objects的初始化很慢,比如NSDateFormatter和NSCalendar。可以通過添加屬性到你的class里或者創(chuàng)建靜態(tài)變量(對(duì)象會(huì)在你的app運(yùn)行時(shí)一直存在于內(nèi)存中,和單例(singleton)很相似)來實(shí)現(xiàn)。
- 選擇正確的數(shù)據(jù)格式,避免反復(fù)處理數(shù)據(jù)
- 選擇正確的數(shù)據(jù)存儲(chǔ)選項(xiàng)
數(shù)據(jù)存儲(chǔ)大致分plist、對(duì)象歸檔、sqlite。NSUserDefault適合存儲(chǔ)小量數(shù)據(jù),私密信息使用Keychain, sqlite適合大量數(shù)據(jù),歸檔性能較低,盡量避免使用。
處理內(nèi)存警告
UIKit提供了幾種收集低內(nèi)存警告的方法:
1、在app delegate中使用applicationDidReceiveMemoryWarning: 的方法
2、在你的自定義UIViewController的子類(subclass)中覆蓋didReceiveMemoryWarning
3、注冊(cè)并接收 UIApplicationDidReceiveMemoryWarningNotification 的通知
代碼優(yōu)化
- 良好的編碼習(xí)慣,遵循代碼規(guī)范、mvc架構(gòu)、利用第三方AFN時(shí),做一層網(wǎng)絡(luò)隔離,減少對(duì)第三方的依賴