1. ?使用ARC進行內(nèi)存管理
ARC除了能避免內(nèi)存泄露外,還有助于程序性能的提升
2.?在適當?shù)那闆r下使用reuseIdentifier
為了使用reuseIdentif iers,在tableview請求一個新的cell時,在數(shù)據(jù)源中調(diào)用下面的方法:
staticNSString*CellIdentifier=@"Cell";
UITableViewCell*cell=[tableViewdequeueReusableCellWithIdentifier:CellIdentifier f orIndexPat h:indexPat h];
3.?盡可能將View設置為不透明(Opaque)
opaque屬性提示繪制系統(tǒng)如何處理view。如果opaque設置為YES,繪圖系統(tǒng)會將view看為完全不透明,這樣繪圖系統(tǒng)就可以 優(yōu)化一些繪制操作以提升性能。如果設置為NO,那么繪圖系統(tǒng)結合其它內(nèi)容來處理view。默認情況下,這個屬性 是YES。
如果屏幕是靜止的,那么這個opaque屬性的設置與否不是一個大問題,但是,如果view是嵌入到scroll view中 的,或者是復雜動畫的一部分,不設置這個屬性的話肯定會影響程序的性能!
4.?避免臃腫的XIBs
如果必須要使用XIBs?的話,盡量讓XIBs?文件簡單。并且每個view controller對于一個XIB文件,如果可以的話,把一個view controller的view不同的層次單獨分到一個XIBs文件中。
當把一個XIB文件加載到內(nèi)存時,XIB文件中的所有內(nèi)容都 將被加載到內(nèi)存中,包括圖片。如果有一個view還不立即使用的 話,就會造成內(nèi)存的浪費。
5.?不要阻塞主線程
永遠都不要在主線程做繁重的任務。因為UIKit?的任務都在主線程中進 行,例如繪制、觸摸管理和輸入響應。如果你的代碼阻塞了主線程,那么程 序?qū)⒊霈F(xiàn)反應遲鈍。在執(zhí)行I/O操作中,大多數(shù)情況下都會阻塞主線程,這些操作需要從外部資源讀寫,例如磁盤或者網(wǎng)絡。
6.?讓圖片的大小跟UIImageView一樣
如果需要將程序bundle中的圖片顯示到UIImageView中,請確保圖 片和UIImageView的大小是一樣的。因為圖片的縮放非常耗費資 源,特別是將UIImageView嵌入到UIScro llView中。
如果是從遠程服務中下載圖片,有時候你控制不了圖片的尺寸, 或者在下載之前無法在服務器上進行圖片的縮放。這種情況,當 圖片下載完之后,你可以手動進行圖片的縮放,最好是在后臺線程中,然后再在UIImageView中使用縮放過的圖片。
7.?選擇正確的集合
數(shù)組:是一個值按順序排列的一個列表。根據(jù)索引可以快速查找,不過根據(jù)值進行查找就比較慢,另外插入和刪除也比較慢。
字典:?存儲鍵/值對。根據(jù)鍵可以快速查找。
Sets:?是一個值無序排列的列表,根據(jù)值可以快速查找,另外插入和刪除也比較快。
8.?使用GZIP壓縮
越來越多的程序依賴于外部數(shù)據(jù),這些數(shù)據(jù)一般來自遠程服務器或者其它的外部APIs?。使用GZIP對網(wǎng)絡傳輸中的數(shù)據(jù)進行壓縮,這樣可以減小文件的大小,并加快下載的速度。壓縮對于文本數(shù)據(jù)特別有用,因為文本具有很高的壓縮比。
9.?重用和延遲加載View
程序界面中包含更多的view,意味著界面在顯示的時候,需要進行更多的繪制任務,也就意味著需要消耗更多的CPU和內(nèi)存資源。特別是在一個UIScro llView里面加入了許多view。 這種情況的管理技巧可以參考UITableView和UICollectionView的行為:不要一次性創(chuàng)建所有的subview,而是在需要的時候在創(chuàng)建view,并且當view使用完畢時候?qū)⑺鼈兲砑拥街赜藐犃兄?。這樣就可以僅在UIScrollView滾動的時候才配置view,以此可以避免分配創(chuàng)建view帶來的成本。
現(xiàn)在有這樣的一個問題:在程序中需要顯示的view在什么時機創(chuàng)建(比如說,當用戶點擊某個按鈕,需要顯示某個view)。這里有兩種可選方法:
1.?在屏幕第一次加載以及隱藏的時候,創(chuàng)建view;然后在需要的時候,再把view顯示出來。
2.?直到需要顯示view的時候,才創(chuàng)建并顯示view。 每種方法都有各自的優(yōu)點和確定。
使用第一種方法,需要消耗更多的內(nèi)容,因為創(chuàng)建出來的view一直占據(jù)著內(nèi)存,直到view被release掉。不過,使用這種方法,當用戶點擊按鈕時,程序會很快的顯示出view,因為只需要修改一下view的可見性即可。
而使用第二種方法則產(chǎn)生相反的效果,當需要的時候才創(chuàng)建view,這會消耗更少的內(nèi)存,不過,當用戶點擊按鈕的時候,不會立即顯示出view。
10. 學會利用緩存
在開發(fā)程序時,一個重要的規(guī)則就是“緩存重要的內(nèi)容”——這些內(nèi)容一般不會改變,并且訪問的頻率比較高。遠程服務器的響應內(nèi)容,圖片,甚至是計算結果,比如UITableView的行高都可用緩存
11.?考慮繪制
在iOS中制作漂亮的按鈕有多種方法??梢允褂萌叽鐖D片,可縮放圖片,或者使用CALayer, CoreGraphics, 甚至是OpenGL來手 動測量和繪制按鈕。
使用預渲染圖片技術是最快的,因為iOS中不用等到在 屏幕上顯示的時候才創(chuàng)建圖形和對形狀進行繪制(圖片已經(jīng)創(chuàng)建 好了!)。這樣帶來的問題是需要把所有的圖片都放到程序bundle中,從而增加了程序的大小。
12.?處理內(nèi)存警告
當系統(tǒng)內(nèi)存偏低時,iOS會通知所有在運行的程序。
UIKit中提供了如下幾種方法來接收低內(nèi)存(low-memory)警告:
(1)實現(xiàn)app delegate中的applicationDidReceiveMemoryWarning:?方法。?
(2)在UIViewController子類中重寫(Override)didReceiveMemoryWarning方法。?
(3)在通知中心里面注冊UIApplicationDidReceiveMemoryWarningNotificatio通知。
在收到以上任意的警告時,需要立即釋放任何不需要的內(nèi)存。
例如,UIViewController的默認情況是清除掉當前不可見的view, 在UIViewController的子類中,可以清除一些額外的數(shù)據(jù)。程序中沒有顯示在當前屏幕中的圖片也可以release掉。
13.?重用花銷很大的對象
有些對象的初始化非常慢——比如NSDateFormatter和NSCalendar。不過有時候可以避免使用這些對象,例如在解析JSON/XML中的日期 時。當使用這些對象時,為了避免性能上的瓶頸,可以嘗試盡量重用這些 對象——在類中添加一個屬性或者創(chuàng)建一個靜態(tài)變量。
注意,如果使用靜態(tài)變量的話,對象會在程序運行的時候一直存在, 就像單例一樣
下面的代碼演示創(chuàng)建一個延遲加載的日期格式屬性。第一次調(diào)用屬性 的時候,會創(chuàng)建一個新的日期格式。之后再調(diào)用的話,會返回已經(jīng)創(chuàng) 建好的實例對象:
// in your .h or inside a class extension
@property (nonatomic, strong) NSDateFormatter *formatter;
//insidetheimplementation(.m)
// When you need, just use self.formatter
?- (NSDateFormatter *)formatter {
if (! _formatter) {
_formatter = [[NSDateFormatter alloc] init];
_formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; // twitter date format
}
return _formatter;?
}
另外,還需要記住的是在設置NSDateFormatter的日期格式時,同樣跟創(chuàng)建新的一個NSDateFormatter實例對象 時一樣慢!因此, 在程序中如果需要頻繁的處理日期格式,那么對NSDateFormatter進行重用是非常好的。
14.?避免重新處理數(shù)據(jù)
許多程序都需要從遠程服務器中獲取數(shù)據(jù),以滿足程序的需求。這些數(shù)據(jù)一般是JSON或XML格式。在請求和接收數(shù)據(jù)時,使用相同的數(shù)據(jù)結構非常重要。為什么呢?在內(nèi)存中把數(shù)據(jù)轉(zhuǎn)換為適合程序的數(shù)據(jù)格式是需要付出額外代價的。
例如,如果你需要在tableview中顯示一些數(shù)據(jù),那么請求和接收的數(shù)據(jù)格式最好是數(shù)組格式的,這樣可以避免一 些中間操作——將數(shù)據(jù)轉(zhuǎn)換為適合程序使用的數(shù)據(jù)結構。
類似的,如果程序是根據(jù)鍵來訪問具體的值,那么最好請求和接收一個鍵/值對字典。
15.?選擇正確的數(shù)據(jù)格式
將數(shù)據(jù)從程序傳到網(wǎng)絡服務器中有多種方法,其中使用的數(shù)據(jù)格式基本都是JSON和XML。你需要做的就是在程序中選擇正確的數(shù)據(jù)格式。JSON的解析速度非常快,并且要比XML小得多,也就意味著只需要傳輸更少數(shù)據(jù)。
16.?設置適當?shù)谋尘皥D片
有兩種方法來給view設置一個背景圖片:
1.?可以使用UIColor的colorWithPatternImge方法來創(chuàng)建一個顏色,并將這個顏色設置為view的背景顏色。
2.?可以給view添加一個UIImageView子視圖。
如果你有一個全尺寸的背景圖片,那么應該使用UIImageView,因為UIColor的colorWithPatternImge方法是用來創(chuàng)建小圖片的——該圖片會被重復使用。此時使用UIImageView會節(jié)省很多內(nèi)存。
UIImageView*backgroundView=[[UIImageViewalloc]initWithImage:[UIImage imageNamed:@"background"]];
[self.view addSubview:backgroundView];
不過,如果你計劃用小圖片當做背景,那么應該使用UIColor的colorWithPatternImge方法。這種情況下繪制速度 會很快,并且不會消耗大量的內(nèi)存。
self.view.backgroundColor=[UIColorcolorWithPatternImage:[UIImage imageNamed:@"background"]];
17.?降低Web內(nèi)容的影響
UIWebView非常有用。用它可以很容易的顯示web內(nèi)容,甚至可以構建UIKit?空間難以顯示的內(nèi)容。不過,你可以能已經(jīng)注意到程序中使用的UIWebView組建沒有蘋果的Saf ari程序快。這是因為JIT?編譯限制了WebKit的Nitro引擎的使用。
因此為了獲得更好的性能,需要調(diào)整一下HTML的大小。首先就是盡量的擺脫JavaScript,并避免使用大的框架, 例如jQuery。有時候使用原始的JavaScript要比別的框架快。
另外,盡量的異步加載JavaScript文件——特別是不直接影響到頁面行為時,例如分析腳本。
最后——讓使用到的圖片,跟實際需要的一樣大小。以此節(jié)省內(nèi)存和提升速度。
18.?設置陰影路徑
如果需要在view活layer中添加一個陰影,該如何處理呢? 大多數(shù)開發(fā)者首先將QuartzCore框架添加到工程中,然后添加如下代碼:
1. #import <QuartzCore/QuartzCore.h> 2.
//Somewherelater...
UIView*view=[[UIViewalloc]init];
5.
// Setup the shadow ...
view.layer.shadowOffset = CGSizeMake(-1.0f, 1.0f);
view.layer.shadowRadius = 5.0f;
view.layer.shadowOpacity=0.6;
然而不幸的是上面這種方法有一個問題。Core Animation在渲染陰影效果之前,必須通過做一個離屏(of f screen)才能確定view的形狀,而這個離屏操作非常耗費資源。
下面有一種方法可以更容易的讓系統(tǒng)進行陰影渲染:設置陰影路徑!
view.layer.shadowPath=[[UIBezierPathbezierPathWithRect:view.bounds]CGPath];
通過設置陰影路徑,iOS就不用總是再計算該如何繪制陰影了。只需要使用你預先計算好的路徑即可。有一點不好的是,根據(jù)view的格式,自己可能很難計算出路徑。另外一個問題就是當view的frame改變時,必須每次都更新一下陰影路徑。
19.?優(yōu)化tableView
tableview需要快速的滾動——如果不能的話,用戶會感覺到停頓。
為了讓table view平滑的滾動,確保遵循了如下建議:
(1)設置正確的reuseIdentifer以重用cell。
(2) 盡量將view設置為不透明,包括cell本身。?
(3)避免漸變,圖像縮放以及離屏繪制。
(4)如果row的高度不相同,那么將其緩存下來。
(5) 如果cell顯示的內(nèi)容來自網(wǎng)絡,那么確保這些內(nèi)容是通過異步來獲取的。?
(6)使用shadowPath來設置陰影。
(7)減少subview的數(shù)量。?
(8)在cellForRowAtIndexPath:中盡量做更少的操作。如果需要做一些處理,那么最好做過一次之后,就將結果緩存起來。
(9)使用適當?shù)臄?shù)據(jù)結構來保存需要的信息。不同的結構會帶來不同的操作代價。
(10)使用rowHeight, sectionFooterHeight?和?sectionHeaderHeight?來設置一個恒定高度, ?而不要從delegate中獲取。
20.?選擇正確的數(shù)據(jù)存儲方式
當需要存儲和讀取大量的數(shù)據(jù)時,該如何選擇存儲方式呢? 有如下選擇:
?(1)使用NSUserDefaults進行存儲保存為XML
(2)JSON或Plist?格式的文件?
(3)利用NSCoding進行歸檔存儲到一個本地數(shù)據(jù)庫,例如SQLit e。?
(4)使用Core Data
雖然NSUserDef aults很好并且容易,不過只只針對于存儲小量數(shù)據(jù)
大量數(shù)據(jù)保存為結構化的文件也可能會帶來問題。一般,在解析這些結構數(shù)據(jù)之前,需要將內(nèi)容全部加載到內(nèi)存 中,這是很消耗資源的。雖然可以使用SAX來處理XML文件,但是這有點復雜。另外,加載到內(nèi)存中的所有對象, 不一定全部都需要用到。
那么使用NSCoding來保存大量數(shù)據(jù)怎么樣呢? 因為它同樣是對文件進行讀寫,因此依然存在上面說的問題。
要保存大量的數(shù)據(jù),最好使用SQLite或Core Data。通過SQLite或Core Data可以進行具體的查詢——只需要獲取
并加載需要的數(shù)據(jù)對象——避免對數(shù)據(jù)進行不合理的搜索。在性能方面,SQLite和Core Data差不大。
SQLite和Core Data最大的區(qū)別實際上就是用法上。Core Data代表一個對象模型,而SQLite只是一個DBMS。
一般,蘋果建議使用Core Data,不過如果你有特殊的原因不能使用Core Data的話,可以使用低級別的SQLite。
在程序中,如果選擇使用SQLite,這里有個方便的庫FMDB?:可以利用該庫操作SQLite數(shù)據(jù)庫,而不用深入使用SQLite C API。
21.?加速啟動時間
(1)讓程序盡量快速啟動的方法就是盡量以異步方式執(zhí)行任務,例如網(wǎng)絡請求,數(shù)據(jù)訪問或解析。
(2)另外,避免使用臃腫的XIBs,因為XIB的加載是在主線程中進行的。
22.?盡量避免Date格式化
如果有許多日期需要使用NSDateFormatter,那么需要小心對待了。如之前(重用花銷很大的對象)所提到的,
無論什么時候,都應該盡量重用NSDateFormatters。
然而,如果你需要更快的速度,那么應該使用C來直接解析日期,而不NSDateFormatter。Sam Sof fes寫了一 篇文章,其中提供了一些解析ISO- 8601格式日期字符的串代碼。你只需要簡單的調(diào)整一下其中的代碼就可以滿足自己特殊的需求了。
如果你自己能控制處理日期的格式,那么可以選擇?Unix timestamps。Unix timestamps是一個簡單的整數(shù),代表 了從新紀元時間(epoch)開始到現(xiàn)在已經(jīng)過了多少秒,通常這個新紀元參考時間是00:00:00 UTC on 1 January 1970。
你可以很容易的見這個時間戳轉(zhuǎn)換為NSDat e,如下所示:
- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {?
? ? ? ? ? ret urn [NSDat e dat eWit hTimeInt ervalSince1970:t imest amp];
?}
上面這個方法比C函數(shù)還要快! 注意:許多網(wǎng)絡APIs返回的時間戳都是毫秒,因此需要注意的是在將這個時間戳傳遞給dateFromUnixTimestamp之前需要除以1000。
關于iOS 啟動性能優(yōu)化可參考:?https://mp.weixin.qq.com/s/Kf3EbDIUuf0aWVT-UCEmbA