初級(jí)
- 使用 ARC 管理內(nèi)存
- 合理使用 reuseIdentifier
- 盡量不要 View 設(shè)置為透明
- 避免過于龐大的 XIB
- 不要阻塞主線程
實(shí)例
- 在 ImageViews 中調(diào)整圖片大小. 如果要在 UIImageView 中顯示一個(gè)來自 bundle 的圖片, 你應(yīng)保證圖片的大小和 UIImageView 的大小相同. 在運(yùn)行中縮放圖譜按是很耗費(fèi)資源的, 特別是 UIImageView 嵌套在 UIScrollView 中的情況. 如果圖片是從遠(yuǎn)端服務(wù)器加載的你不能控制圖片的大小, 比如在下載器前調(diào)整到合適大小的話, 你可以再狹窄完成后, 最好是用 background thread, 縮放一次, 然后在 UIImageView中是用縮放后的圖片
- 選擇正確的Collection
- Array: 有序的一組值. 使用 index 來 lookup 很快, 使用 value lookup 很慢, 插入/刪除很慢
- Dictionaries: 儲(chǔ)存鍵值對(duì), 用 key 來查找會(huì)很快
- Sets: 無序的一組值. 用值來查找會(huì)快, 插入/刪除很快.
- 打開 gzip 壓縮. app 可能大連依賴于服務(wù)器資源, 問題是我們的目標(biāo)是移動(dòng)設(shè)備, 因此你就不能指望網(wǎng)絡(luò)狀況有多好. 減小文檔的一個(gè)方式就是在服務(wù)端和你的 app 中打開 gzip. 這對(duì)于文字種種能有更高壓縮率的數(shù)據(jù)來說會(huì)有更顯著的效果. iOS 已經(jīng)在 NSURLConnection 中默認(rèn)支持了 gzip 壓縮, 當(dāng)然 AFNetworking 這些基于它的框架也是.
中級(jí)
- 重用和延遲加載(lazy load) Views
- 更多的View意味著更多的渲染, 也就是更多的 CPU的內(nèi)存消耗, 對(duì)于那種嵌套了很多View 在UIScrollView 你變的 app 更是如此.
- 這里我們用到的技巧就是模仿 UITableView 和 UICollectionView 的操作: 不要一次創(chuàng)建所有的 subview, 二十當(dāng)需要的時(shí)候才創(chuàng)建, 當(dāng)他們完成使命, 就把他們放進(jìn)一個(gè)可從用的隊(duì)列中. 這樣的話你就只需要在滾動(dòng)發(fā)生的時(shí)候創(chuàng)建你的 views, 避免不劃算的內(nèi)存分配.
- cache
- 一個(gè)極好的原則就是, 緩存所需要的, 也就是那些不大可能改變你當(dāng)時(shí)需要經(jīng)常讀取的東西.
- 可以緩存的東西, 遠(yuǎn)端服務(wù)器的響應(yīng), 圖片, 計(jì)算記過, UITableView 的行高.
- NSCache 和 NSDictionary 類似, 不同的是系統(tǒng)回收內(nèi)存的時(shí)候他會(huì)自動(dòng)刪掉它的內(nèi)容.
- 權(quán)衡渲染方法. 性能還是 bundle保持合適的大小.
- 處理內(nèi)存警告. 移除緩存, 圖片objc和其他一些可以重新創(chuàng)建的 object 和 strong references.
- 重用大開銷對(duì)象
- 一些 Object 的初始化很慢, 比如 NSDateFormatter 和 NSCalendar. 然而, 你又不可避免的需要使用它們, 比如從 JSON 或者 XML 中解析數(shù)據(jù). 想要避免使用這個(gè)對(duì)象的瓶頸, 你就需要重用它們, 可以通過添加屬性到你的 Calss 里或者創(chuàng)建今天變量實(shí)現(xiàn).
- 避免反復(fù)處理數(shù)據(jù). 在服務(wù)器端和客戶端使用相同的數(shù)據(jù)結(jié)構(gòu)很重要.
- 選擇正確的數(shù)據(jù)格式. 解析JSON 會(huì)比 XML 更快一些, JSON 也通常更小更便于傳輸. 從iOS5起有了官方內(nèi)奸的 JSON deserialization 就更加方便使用了. 但是 XML 也有 XML 的好處, 比如使用 SAX 來解析本地文件一樣, 你不需要像解析 json 一樣等到整個(gè)文檔下載完成才開始解析. 當(dāng)你處理很大的數(shù)據(jù)的時(shí)候就會(huì)極大的降低內(nèi)存消耗和增加性能.
- 正確設(shè)定背景圖片
- 全屏背景圖, 在 View 中添加一個(gè) UIImageView 作為一個(gè)子 View
- 只是某個(gè)小的 View 的背景圖, 你就需要用 UIColor 的 colorWithPatternImage 來做了, 它會(huì)更快地渲染也不會(huì)花費(fèi)很多內(nèi)存.
- 減少使用 web 特性. 想要更高的性能你就要調(diào)整下你的 HTML 了, 第一件要做的事就是盡可能移除不必要的 JavaScript, 避免使用過大的框架. 能只用原生的 js 就更好了. 盡可能異步加載例如用戶行為統(tǒng)計(jì) script 這種不影響頁面表達(dá)的 javaScript. 注意你使用的圖片, 保證圖片符合你使用的大小.
- shadow Path. Core Animation 不得不現(xiàn)在后臺(tái)得出你的圖形并加好陰影然后渲染, 這個(gè)開銷是很大的. 使用shadowPath 的話就避免了這個(gè)問題. 使用 shadow path 的話 iOS 就不必每次都計(jì)算如何渲染, 它使用一個(gè)預(yù)先計(jì)算好的路徑. 但問題都是自己計(jì)算path的話可能在默寫 View 中比較困難, 且每當(dāng) View 的 frame 變化的時(shí)候你都需要去update shadow path
- 優(yōu)化 TableView
- 正確使用 reuseIdentifier 來重用 cell
- 盡量使所有的 View opaque, 包括 cell 自身
- 避免漸變, 圖片縮放, 后臺(tái)選人
- 緩存行高
- 如果 cell 內(nèi)部的內(nèi)容來自web, 使用異步加載, 緩存請(qǐng)求結(jié)果.
- 使用 shadowPath 來畫陰影
- 減少 subviews 的數(shù)量
- 盡量不使用 cellForRowAtIndexPath: 如果你需要用到它, 只用一次然后緩存結(jié)果
- 使用正確的數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)數(shù)據(jù)
- 使用 towHeight, sectionFooterHeight 和 sectionHeaderHeight 來 設(shè)定固定的高, 不要請(qǐng)求delegate
- 選擇正確的數(shù)據(jù)儲(chǔ)存選項(xiàng)
- NSUserDefaults: 很nice 也很便捷, 但是它只適用于小數(shù)據(jù), 比如一些簡(jiǎn)單的布爾型的設(shè)置選項(xiàng), 再大點(diǎn)你就要考慮其他方式了
- XML 這種結(jié)構(gòu)化檔案: 總體來說, 你需要讀取整個(gè)文件到內(nèi)存里去解析, 這樣是很不經(jīng)濟(jì)的, 適用 SAX 有事一個(gè)很麻煩的事情.
- NSCoding: 也需要讀取文件
- 在性能層面來說, SQLite 和 CoreData 是相似的, 他們的不同在于具體使用方法
- Core Data 代表一個(gè)對(duì)象的 graph model, 但是 SQLite 就是一個(gè) DBMS
- Apple 在一般情況下建議使用 Core Data, 但是如果你有理由不使用它, 那么久去使用更加底層的 SQLite 吧
- 如果你使用 SQLite, 你就可以用FMDB 這個(gè)庫來簡(jiǎn)化 SQLite 的操作, 這樣你就不會(huì)花很多精力了解 SQLite 的 C API了
高級(jí)
- 加速啟動(dòng)時(shí)間. 快速打開app是很重要的, 特別是用戶第一次打開它時(shí), 對(duì) app 來講, 第一印象太重要了, 和人見面一樣. 你能做的就是使它盡可能做更多的異步任務(wù), 比如加載遠(yuǎn)端或者數(shù)據(jù)庫數(shù)據(jù), 解析數(shù)據(jù). 避免過于龐大的 XIB, 因?yàn)樗麄兪窃谥骶€程上加載的. 所以盡量使用沒有這個(gè)問題的 StoryBoards. 一定要把設(shè)備從 Xcode 斷奶來測(cè)試啟動(dòng)速度
- 使用 Autorelease Pool. NSAutoreleasePool 負(fù)責(zé)釋放 block 中的 autoreleased Object. 一般情況下它會(huì)自動(dòng)被 UIKit 調(diào)用. 但是有些情況下也需要手動(dòng)穿吉他. 加入創(chuàng)建很多零食對(duì)象, 你會(huì)發(fā)現(xiàn)內(nèi)存一直在減少直到這些對(duì)象被 release 的時(shí)候. 這是因?yàn)橹挥挟?dāng) UIKit 用光了 autorelease pool 的時(shí)候 memory 才會(huì)被釋放. 消息是你可以在自己的 @autoreleasepool 里創(chuàng)建臨時(shí)的對(duì)象來避免這個(gè)行為.
- 選擇是否緩存圖片. 常見的從 bundle 中加載圖片的方式有倆種, 一個(gè)是 imageNamed, 一個(gè)是 imageWithContentsOfFile,
- imageNamed: 這個(gè)方法用一個(gè)指定的名字在系統(tǒng)緩存中查找并返回一個(gè)圖片對(duì)象如果它存在的話。如果緩存中沒有找到相應(yīng)的圖片,這個(gè)方法從指定的文檔中加載然后緩存并返回這個(gè)對(duì)象。因此imageNamed的優(yōu)點(diǎn)是當(dāng)加載時(shí)會(huì)緩存圖片。所以當(dāng)圖片會(huì)頻繁的使用時(shí),那么用imageNamed的方法會(huì)比較好。例如:你需要在 一個(gè)TableView里的TableViewCell里都加載同樣一個(gè)圖標(biāo),那么用imageNamed加載圖像效率很高。系統(tǒng)會(huì)把那個(gè)圖標(biāo)Cache到內(nèi)存,在TableViewCell里每次利用那個(gè)圖像的時(shí)候,只會(huì)把圖片指針指向同一塊內(nèi)存。正是因此使用imageNamed會(huì)緩存圖片,即將圖片的數(shù)據(jù)放在內(nèi)存中,iOS的內(nèi)存非常珍貴并且在內(nèi)存消耗過大時(shí),會(huì)強(qiáng)制釋放內(nèi)存,即會(huì)遇到memory warnings。而在iOS系統(tǒng)里面釋放圖像的內(nèi)存是一件比較麻煩的事情,有可能會(huì)造成內(nèi)存泄漏。例如:當(dāng)一個(gè)UIView對(duì)象的animationImages是一個(gè)裝有UIImage對(duì)象動(dòng)態(tài)數(shù)組NSMutableArray,并進(jìn)行逐幀動(dòng)畫。當(dāng)使用imageNamed的方式加載圖像到一個(gè)動(dòng)態(tài)數(shù)組NSMutableArray,這將會(huì)很有可能造成內(nèi)存泄露。原因很顯然的。
- imageWithContentsOfFile:僅加載圖片,圖像數(shù)據(jù)不會(huì)緩存。因此對(duì)于較大的圖片以及使用情況較少時(shí),那就可以用該方法,降低內(nèi)存消耗。
- 避免日期格式的轉(zhuǎn)換. 如果你要用 NSDataFormatter 來處理很多日期格式, 應(yīng)該小心一點(diǎn). 就像之前提到的, 任何時(shí)候重用 NSDateFormatters 都是一個(gè)好的時(shí)間. 如果你可以控制你所處理的日期格式, 今年選擇 Unix 時(shí)間戳. 你可以方便的從時(shí)間戳轉(zhuǎn)換到 NSDate
- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {
return[NSDate dateWithTimeIntervalSince1970:timestamp];
}
這樣會(huì)比用 C 來解析日期字符串還快, 需要注意的是, 許多 web PAI 會(huì)以未免的形式返回時(shí)間戳, 因?yàn)檫@種格式在 javascript 中更方便使用. 記住用 dateFromUnixTimestamp 之前除以 1000 就可以了