性能優(yōu)化
參考文章:
http://www.itdecent.cn/p/c51106cbea05
一、tableView的優(yōu)化
1、使用tableViewCell復(fù)用,利用緩沖池。 每次刷新顯示都會(huì)去創(chuàng)建新的Cell,非常耗費(fèi)性能。
解決方案:首先創(chuàng)建一個(gè)靜態(tài)變量reuseID(代理方法返回Cell會(huì)調(diào)用很多次,防止重復(fù)創(chuàng)建,static保證只會(huì)被創(chuàng)建一次,提高性能),然后,從緩存池中取相應(yīng)identifier的Cell并更新數(shù)據(jù),如果沒有,才開始alloc新的Cell,并用identifier標(biāo)識(shí)Cell。每個(gè)Cell都會(huì)注冊(cè)一個(gè)identifier(重用標(biāo)識(shí)符)放入緩存池,當(dāng)需要調(diào)用的時(shí)候就直接從緩存池里找對(duì)應(yīng)的id,當(dāng)不需要時(shí)就放入緩存池等待調(diào)用。(移出屏幕的Cell才會(huì)放入緩存池中,并不會(huì)被release)所以在數(shù)據(jù)源方法中做出如下優(yōu)化:
2、heightForRow
如果是動(dòng)態(tài)計(jì)算的,將高度存入模型中。避免每次計(jì)算
3、異步繪制
如果需要對(duì)圖片進(jìn)行圓角處理等,可以通過CGContextRef方法性能更高
4、避免大量的圖片縮放、顏色漸變盡量顯示“大小剛好合適的圖片資源”
5、避免同步的從網(wǎng)絡(luò)、文件獲取數(shù)據(jù),Cell內(nèi)實(shí)現(xiàn)的內(nèi)容來(lái)自web,使用異步加載,緩存請(qǐng)求結(jié)果
6、渲染
a.減少subviews的個(gè)數(shù)和層級(jí)
子控件的層級(jí)越深,渲染到屏幕上所需要的計(jì)算量就越大;如多用drawRect繪制元素,替代用view顯示
b.少用subviews的透明圖層
對(duì)于不透明的View,設(shè)置opaque為YES,這樣在繪制該View時(shí),就不需要考慮被View覆蓋的其他內(nèi)容(盡量設(shè)置Cell的view為opaque,避免GPU對(duì)Cell下面的內(nèi)容也進(jìn)行繪制)
c.避免CALayer特效(shadowPath)
給Cell中View加陰影會(huì)引起性能問題,如下面代碼會(huì)導(dǎo)致滾動(dòng)時(shí)有明顯的卡頓:
二、程序啟動(dòng)優(yōu)化
1、開啟時(shí)間分析功能
在Xcode的菜單中選擇Project→Scheme→Edit Scheme...,然后找到Run → Environment Variables → +,添加name為DYLD_PRINT_STATISTICSvalue為1的環(huán)境變量。
2、結(jié)論分析
Total pre-main time: 94.33 milliseconds (100.0%)
dylib loading time: 61.87 milliseconds (65.5%)
rebase/binding time: 3.09 milliseconds (3.2%)
ObjC setup time: 10.78 milliseconds (11.4%)
initializer time: 18.50 milliseconds (19.6%)
slowest intializers :
libSystem.B.dylib : 3.59 milliseconds (3.8%)
libBacktraceRecording.dylib : 3.65 milliseconds (3.8%)
MyTest : 7.09 milliseconds (7.5%)
main()函數(shù)之前總共使用了94.33ms
在94.33ms中,加載動(dòng)態(tài)庫(kù)用了61.87ms,指針重定位使用了3.09ms,ObjC類初始化使用了10.78ms,各種初始化使用了18.50ms。
在初始化耗費(fèi)的18.50ms中,用時(shí)最多的三個(gè)初始化是libSystem.B.dylib、libBacktraceRecording.dylib以及MyTest。
動(dòng)態(tài)庫(kù)、ObjC 和ObjC的+load方法越多啟動(dòng)越慢
三、其他優(yōu)化建議
1.不要阻塞主線程
2.盡可能設(shè)置 View 為不透明
3.避免臃腫的 XIB 文件
4.啟用 GZIP 數(shù)據(jù)壓縮
5.View 的復(fù)用、懶加載
6、緩存
服務(wù)器的響應(yīng)信息(response)、圖片、計(jì)算值。比如:UITableView 的 row heights。
7.處理內(nèi)存警告
在 AppDelegate 中實(shí)現(xiàn) - [AppDelegate applicationDidReceiveMemoryWarning:] 代理方法。
在 UIViewController 中重載 didReceiveMemoryWarning 方法。
監(jiān)聽 UIApplicationDidReceiveMemoryWarningNotification 通知。
8.復(fù)用高開銷的對(duì)象
9.減少應(yīng)用啟動(dòng)時(shí)間
快速啟動(dòng)應(yīng)用對(duì)于用戶來(lái)說(shuō)可以留下很好的印象。尤其是第一次使用時(shí)。
保證應(yīng)用快速啟動(dòng)的指導(dǎo)原則:
盡量將啟動(dòng)過程中的處理分拆成各個(gè)異步處理流,比如:網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)庫(kù)訪問、數(shù)據(jù)解析等等。
避免臃腫的 XIB 文件,因?yàn)樗鼈儠?huì)在你的主線程中進(jìn)行加載。重申:Storyboard 沒這個(gè)問題,放心使用。
注意:在測(cè)試程序啟動(dòng)性能的時(shí)候,最好用與 Xcode 斷開連接的設(shè)備進(jìn)行測(cè)試。因?yàn)?watchdog 在使用 Xcode 進(jìn)行調(diào)試的時(shí)候是不會(huì)啟動(dòng)的。
10.使用 Autorelease Pool (內(nèi)存釋放池)
11.imageNamed 和 imageWithContentsOfFile
imageNamed:創(chuàng)建的對(duì)象會(huì)緩存到系統(tǒng)內(nèi)存中,不會(huì)立即釋放到內(nèi)存。好處是再次加載使用這種方式會(huì)減少讀取操作,加快程序運(yùn)行。缺點(diǎn):加載過多圖片會(huì)占用大量?jī)?nèi)存空間。
mageWithContentsOfFile創(chuàng)建的對(duì)象不會(huì)緩存到系統(tǒng)內(nèi)存中。好處是不產(chǎn)生緩存。缺點(diǎn):對(duì)于經(jīng)常使用的小圖片,會(huì)頻繁讀取。
加載圖片:
1->如果在項(xiàng)目中的Assets.xcassets(藍(lán)色文件夾)
a.不可以NSBundle獲得資源路徑,然后imageWithContentsOfFile加載
b.可以imageNamed加載
2->如果在項(xiàng)目中真實(shí)文件夾(藍(lán)色文件夾,除Assets.xcassets):
a.可以NSBundle獲得資源路徑,然后imageWithContentsOfFile加載。注意要帶文件夾路徑,例如
[[NSBundle mainBundle]pathForResource:@"test.jpg" ofType:nil inDirectory:@"image"]
b.不可以imageNamed加載
3->如果在項(xiàng)目中虛擬文件夾(黃色文件夾)
a.可以NSBundle獲得資源路徑,然后imageWithContentsOfFile加載
b.可以imageNamed加載
12、延遲加載圖片
有時(shí)候在邊滾動(dòng)邊設(shè)置圖片的時(shí)候可能會(huì)有一定的影響,因此可以在滾動(dòng)的時(shí)候imageview不執(zhí)行setimage的操作,滾動(dòng)停止的時(shí)候才加載圖片,由于滾動(dòng)的時(shí)候NSRunloop是處于UITrackingRunLoopMode模式下,可以采用如下的方式,將設(shè)置圖片放到NSDefaultRunLoopMode模式下才進(jìn)行:
UIImage *downloadedImage = ...;
[self.avatarImageView performSelector:@selector(setImage:)
withObject:downloadedImage
afterDelay:0
inModes:@[NSDefaultRunLoopMode]];
13.減少離屏渲染(設(shè)置圓角和陰影的時(shí)候可以選用繪制的方法)
14.優(yōu)化 UITableView
通過正確的設(shè)置 reuseIdentifier 來(lái)重用 Cell。
盡量減少不必要的透明 View。
盡量避免漸變效果、圖片拉伸和離屏渲染。
當(dāng)不同的行的高度不一樣時(shí),盡量緩存它們的高度值。
如果 Cell 展示的內(nèi)容來(lái)自網(wǎng)絡(luò),確保用異步加載的方式來(lái)獲取數(shù)據(jù),并且緩存服務(wù)器的 response。
使用 shadowPath 來(lái)設(shè)置陰影效果。
盡量減少 subview 的數(shù)量,對(duì)于 subview 較多并且樣式多變的 Cell,可以考慮用異步繪制或重寫 drawRect。
盡量?jī)?yōu)化 - [UITableView tableView:cellForRowAtIndexPath:] 方法中的處理邏輯,如果確實(shí)要做一些處理,可以考慮做一次,緩存結(jié)果。
選擇合適的數(shù)據(jù)結(jié)構(gòu)來(lái)承載數(shù)據(jù),不同的數(shù)據(jù)結(jié)構(gòu)對(duì)不同操作的開銷是存在差異的。
對(duì)于 rowHeight、sectionFooterHeight、sectionHeaderHeight 盡量使用常量。
15.選擇合適的數(shù)據(jù)存儲(chǔ)方式
在 iOS 中可以用來(lái)進(jìn)行數(shù)據(jù)持有化的方案包括:
NSUserDefaults。只適合用來(lái)存小數(shù)據(jù)。
XML、JSON、Plist 等文件。JSON 和 XML 文件的差異在「選擇正確的數(shù)據(jù)格式」已經(jīng)說(shuō)過了。
使用 NSCoding 來(lái)存檔。NSCoding 同樣是對(duì)文件進(jìn)行讀寫,所以它也會(huì)面臨必須加載整個(gè)文件才能繼續(xù)的問題。
使用 SQLite 數(shù)據(jù)庫(kù)。可以配合 FMDB 使用。數(shù)據(jù)的相對(duì)文件來(lái)說(shuō)還是好處很多的,比如可以按需取數(shù)據(jù)、不用暴力查找等等。
使用 CoreData。也是數(shù)據(jù)庫(kù)技術(shù),跟 SQLite 的性能差異比較小。但是 CoreData 是一個(gè)對(duì)象圖譜模型,顯得更面向?qū)ο螅籗QLite 就是常規(guī)的 DBMS。
16.減少應(yīng)用啟動(dòng)時(shí)間
快速啟動(dòng)應(yīng)用對(duì)于用戶來(lái)說(shuō)可以留下很好的印象。尤其是第一次使用時(shí)。
保證應(yīng)用快速啟動(dòng)的指導(dǎo)原則:
盡量將啟動(dòng)過程中的處理分拆成各個(gè)異步處理流,比如:網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)庫(kù)訪問、數(shù)據(jù)解析等等。
避免臃腫的 XIB 文件,因?yàn)樗鼈儠?huì)在你的主線程中進(jìn)行加載。重申:Storyboard 沒這個(gè)問題,放心使用。
注意:在測(cè)試程序啟動(dòng)性能的時(shí)候,最好用與 Xcode 斷開連接的設(shè)備進(jìn)行測(cè)試。因?yàn)?watchdog 在使用 Xcode 進(jìn)行調(diào)試的時(shí)候是不會(huì)啟動(dòng)的。
17.使用 Autorelease Pool (內(nèi)存釋放池)
18.imageNamed 和 imageWithContentsOfFile
19.對(duì)于圓角可以使用一張中間圓形透明的圖覆蓋在上面,雖然這會(huì)引入blending操作,但是大部分情況下性能會(huì)比離屏渲染好。
20.利用枚舉遍歷數(shù)組,效率比f(wàn)or循環(huán)高
21.加載圖片時(shí)用imageWithContentsOfFiles 這樣不會(huì)講圖片緩存到內(nèi)存中,同時(shí)要避免圖片縮放,因?yàn)檫@是很消耗性能
22.處理內(nèi)存警告
UIViewController的默認(rèn)行為是移除一些不可見的view,這樣對(duì)內(nèi)存警報(bào)的處理是很必要的,若不重視,你的app就可能被系統(tǒng)殺掉。