IPhone下每個(gè)app可用的內(nèi)存是被限制的,,如果一個(gè)app使用的內(nèi)存超過(guò)20M,,則系統(tǒng)會(huì)向該app發(fā)送Memory
Warning消息。蘋(píng)果公司系統(tǒng)工程師建議,,應(yīng)用程序所占內(nèi)存不應(yīng)該超過(guò)20MB,,開(kāi)發(fā)人員圈內(nèi)流傳著一個(gè)粗略的經(jīng)驗(yàn)法則:當(dāng)應(yīng)用程序占用了大約20MB內(nèi)存時(shí),iphone開(kāi)始發(fā)出內(nèi)存警告,。當(dāng)應(yīng)用程序所占內(nèi)存大約為30MB時(shí),,iphone
OS會(huì)關(guān)閉應(yīng)用程序。收到此消息后,,app必須正確處理,,否則可能出錯(cuò)或者出現(xiàn)內(nèi)存泄露。app收到Memory
Warning后會(huì)調(diào)用:UIApplication::didReceiveMemoryWarning
->
UIApplicationDelegate::applicationDidReceiveMem 我們知道,,創(chuàng)建viewcontroller時(shí),執(zhí)行順序是loadview
-> 當(dāng)重新顯示該viewController時(shí),,執(zhí)行過(guò)viewDidUnLoad的viewcontroller(即原來(lái)在后臺(tái))會(huì)重新調(diào)用loadview -> viewDidLoad,。
重載didReceiveMemoryWarning時(shí),一定調(diào)用這個(gè)函數(shù)的super實(shí)現(xiàn)來(lái)允許父類(lèi)(一般是UIVIewController)釋放self.view,。self.view釋放之后,,會(huì)調(diào)用下面的viewDidUnload函數(shù).也就是說(shuō),盡管self.view是被處理了,,但是outlets的變量因?yàn)楸籸etain過(guò),,所以不會(huì)被釋放,為了解決這個(gè)問(wèn)題,,就需要在viewDidUnload中釋放這些retain過(guò)的outlets變量,。通常controller會(huì)保存nib文件建立的views的引用,但是也可能會(huì)保存著loadView函數(shù)創(chuàng)建的對(duì)象的引用,。最完美的方法是使用合成器方法: 因此主要注意下面幾個(gè)函數(shù):
實(shí)際中如果viewcontroller是用xib生成的界面,則需要我們做的就比較少,,主要是在viewDidLoad中恢復(fù)原來(lái)的界面狀態(tài),。 如果是通過(guò)編程創(chuàng)建的界面,則需要做的工作就要更多些,,上面4個(gè)函數(shù)中都需要進(jìn)行正確處理,。 iOS6.0及其以后,viewDidUnload不再有用,,收到low-memeory時(shí)系統(tǒng)不會(huì)釋放Views,。iOS6.0及以上版本的內(nèi)存警告: 調(diào)用didReceiveMemoryWarning內(nèi)調(diào)用super的didReceiveMemoryWarning調(diào)只是釋放controller的resouse,不會(huì)釋放view 處理方法: -(void)didReceiveMemoryWarning { } 但是似乎這么寫(xiě)相對(duì)于以前并不省事。最終我們找到一篇文章,,文章中說(shuō)其實(shí)并不值得回收這部分的內(nèi)存,,原因如下: 1. UIView是UIResponder的子類(lèi),而UIResponder有一個(gè)CALayer的成員變量,,CALayer是具體用于將自己畫(huà)到屏幕上的,。 2. CALayer是一個(gè)bitmap圖象的包裝類(lèi),當(dāng)UIView調(diào)用自身的drawRect時(shí),,CALayer才會(huì)創(chuàng)建這個(gè)bitmap圖象類(lèi),。 3. 具體占內(nèi)存的其實(shí)是一個(gè)bitmap圖象類(lèi),CALayer只占48bytes, UIView只占96bytes,。而一個(gè)iPad的全屏UIView的bitmap類(lèi)會(huì)占到12M的大?。?br> 4.在iOS6時(shí),,當(dāng)系統(tǒng)發(fā)出MemoryWarning時(shí),系統(tǒng)會(huì)自動(dòng)回收bitmap類(lèi),。但是不回收UIView和CALayer類(lèi),。這樣即回收了大部分內(nèi)存,又能在需要bitmap類(lèi)時(shí),,根據(jù)CALayer類(lèi)重建,。 所以,iOS6這么做的意思是:我們根本沒(méi)有必要為了幾十byte而費(fèi)力回收內(nèi)存,。 移動(dòng)設(shè)備終端的內(nèi)存極為有限,,應(yīng)用程序必須做好low-memory處理工作,,才能避免程序因內(nèi)存使用過(guò)大而崩潰。 low-memory 處理思路 通 常一個(gè)應(yīng)用程序會(huì)包含多個(gè)view controllers,,當(dāng)從view跳轉(zhuǎn)到另一個(gè)view時(shí),,之前的view只是不可見(jiàn)狀態(tài),并不會(huì)立即被清理掉,,而是保存在內(nèi)存中,,以便下一次的快速 顯現(xiàn)。但是如果應(yīng)用程序接收到系統(tǒng)發(fā)出的low-memory warning,,我們就不得不把當(dāng)前不可見(jiàn)狀態(tài)下的views清理掉,,騰出更多的可使用內(nèi)存;當(dāng)前可見(jiàn)的view controller也要合理釋放掉一些緩存數(shù)據(jù),,圖片資源和一些不是正在使用的資源,,以避免應(yīng)用程序崩潰。 思路是這樣,,具體的實(shí)施根據(jù)系統(tǒng)版本不同而略有差異,,本文將詳細(xì)說(shuō)明一下iOS 5與iOS 6的low-memory處理。 iOS 5 的處理 在iOS 6 之前,,如果應(yīng)用程序接收到了low-memory警告,,當(dāng)前不可見(jiàn)的view controllers會(huì)接收到viewDidUnload消息(也可以理解為自動(dòng)調(diào)用viewDidUnload方法),所以我們需要在 viewDidUnload 方法中釋放掉所有 outlets ,,以及可再次創(chuàng)建的資源,。當(dāng)前可見(jiàn)的view controller 通過(guò)didReceiveMemoryWarning 合理釋放資源,具體見(jiàn)代碼注釋,。 舉一個(gè)簡(jiǎn)單的例子,有這樣一個(gè)view controller: @interface MyViewController : UIViewController { } @property (nonatomic, strong) IBOutlet UITableView *tableView; @end 對(duì)應(yīng)的處理則為: #pragma mark - #pragma mark Memory management - (void)didReceiveMemoryWarning { } - (void)viewDidUnload { } iOS 6 的處理 iOS 6 廢棄了viewDidUnload方法,,這就意味著一切需要我們自己在didReceiveMemoryWarning中操作。 具體應(yīng)該怎么做呢,? 1.將 outlets 置為 weak 當(dāng)view dealloc時(shí),,沒(méi)有人握著任何一個(gè)指向subviews的強(qiáng)引用,那么subviews實(shí)例變量將會(huì)自動(dòng)置空,。 @property (nonatomic, weak) IBOutlet UITableView *tableView; 2.在didReceiveMemoryWarning中將緩存數(shù)據(jù)置空 #pragma mark - #pragma mark Memory management - (void)didReceiveMemoryWarning { } 不要忘記一點(diǎn),,每當(dāng)tableview reload 的時(shí)候,需要判斷一下 dataArray ,,若為空則重新創(chuàng)建,。 兼容iOS 5 與 iOS 6 好吧,重點(diǎn)來(lái)了,,倘若希望程序兼容iOS 5 與 iOS 6怎么辦呢,? 這里有一個(gè)小技巧,我們需要對(duì)didReceiveMemoryWarning 做一些手腳: #pragma mark - #pragma mark Memory management - (void)didReceiveMemoryWarning { } 判斷一下view是否是window的一部分,如果不是,,那么可以放心的將self.view 置為空,,以換取更多可用內(nèi)存。 這 樣會(huì)是什么現(xiàn)象呢,?假如,,從view controller A 跳轉(zhuǎn)到 view controller B ,然后模擬low-memory警告,此時(shí),,view controller A 將會(huì)執(zhí)行self.view = nil ; 當(dāng)我們從 B 退回 A 時(shí),, A 會(huì)重新調(diào)用一次 viewDidLoad ,此時(shí)數(shù)據(jù)全部重新創(chuàng)建,,簡(jiǎn)單兼容無(wú)壓力~~ Note: 如果你好奇Apple為什么廢棄viewDidUnload,,可以看看Apple 的解釋?zhuān)?br> Apple deprecated viewDidUnload for a good reason. The memory savings from setting a few outlets to nil just weren’t worth it and added a lot of complexity for little benefit. For iOS 6+ apps, you can simply forget about view unloading and only implement didReceiveMemoryWarning if the view controller can let go of cached data that you can recreate on demand later. 原文地址:http://justsee./blog/1820588 官方文檔:https://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhon ViewController的生命周期和didReceiveMemoryWarning后的流程:http://blog.csdn.net/iunion/article/details/8699491 ViewController的生命周期中各方法執(zhí)行流程如下: 跟隨如下文字理解viewController對(duì)view加載過(guò)程: 1 先判斷子類(lèi)是否重寫(xiě)了loadView,如果有直接調(diào)用,。之后調(diào)viewDidLoad完成View的加載,。 2 如果是外部通過(guò)調(diào)用initWithNibName:bundle指定nib文件名的話(huà),ViewController記載此nib來(lái)創(chuàng)建View,。 3 如果initWithNibName:bundle的name參數(shù)為nil,,則ViewController會(huì)通過(guò)以下兩個(gè)步驟找到與其關(guān)聯(lián)的nib。 A 如果類(lèi)名包含Controller,,例如ViewController的類(lèi)名是MyViewController,,則查找是否存在MyView.nib; B 找跟ViewController類(lèi)名一樣的文件,,例如MyViewController,,則查找是否存在MyViewController.nib。 4 如果子類(lèi)沒(méi)有重寫(xiě)的loadView,,則ViewController會(huì)從StroyBoards中找或者調(diào)用其默認(rèn)的loadView,,默認(rèn)的loadView返回一個(gè)空白的UIView對(duì)象。 注意第一步,,ViewController是判斷子類(lèi)是否重寫(xiě)了loadView,,而不是判斷調(diào)用子類(lèi)的loadView之后ViewController的View是否為空。就是說(shuō),,如果子類(lèi)重寫(xiě)了loadView的話(huà),,不管子類(lèi)在loadView里面能否獲取到View,ViewController都會(huì)直接調(diào)viewDidLoad完成View的加載
那為什么要寫(xiě)成 self.myOutlet = nil; ,,實(shí)際上這個(gè)語(yǔ)法是執(zhí)行了 property 里的setter 方法,,而不是一個(gè)簡(jiǎn)單的變量賦值,它干了兩件事:1,、老數(shù)據(jù) release 掉,2、新數(shù)據(jù)(nil)retain(當(dāng) property 設(shè)置為 retain 的情況下),,當(dāng)然對(duì) nil retain 是無(wú)意義的,。如果寫(xiě)成 myOutlet = nil,那就是簡(jiǎn)單的把 myOutlet 指向 nil,,這樣內(nèi)存就泄漏了,,因?yàn)槔蠑?shù)據(jù)沒(méi)有 release。而如果僅僅寫(xiě)成 [myOutlet release] 也會(huì)有問(wèn)題,,因?yàn)楫?dāng) view 被 dealloc 的時(shí)候會(huì) 再次 release,,程序就出錯(cuò)了,而對(duì) nil release 是沒(méi)有問(wèn)題的,。 dealloc 是當(dāng)前 viewController 被釋放的時(shí)候,,清空所有當(dāng)前
viewController 里面的實(shí)體和數(shù)據(jù)來(lái)釋放內(nèi)存,該方法也是自動(dòng)調(diào)用的,,無(wú)需手動(dòng)執(zhí)行,。舉例說(shuō)明當(dāng) modalView 被
dismissModalViewControll 在開(kāi)發(fā)iOS應(yīng)用程序時(shí),,讓程序具有良好的性能是非常關(guān)鍵的,。這也是用戶(hù)所期望的,如果你的程序運(yùn)行遲鈍或緩慢,,會(huì)招致用戶(hù)的差評(píng),。
然而由于iOS設(shè)備的局限性,有時(shí)候要想獲得良好的性能,,是很困難的,。在開(kāi)發(fā)過(guò)程中,有許多事項(xiàng)需要記住,,并且關(guān)于性能影響很容易就忘記,。 這就是為什么我要寫(xiě)這篇文章!本文收集了25個(gè)關(guān)于可以提升程序性能的提示和技巧。
目錄
我把性能優(yōu)化技巧分為3個(gè)不同的等級(jí):初級(jí),、中級(jí)和高級(jí):
高級(jí) 當(dāng)且僅當(dāng)下面這些技巧能夠解決問(wèn)題的時(shí)候,,才使用它們:
加速啟動(dòng)時(shí)間
使用Autorelease Pool
緩存圖片 — 或者不緩存 盡量避免Date格式化 高級(jí)性能提升
尋找一些高明的方法,讓自己變?yōu)橐粋€(gè)全代碼忍者?下面這些高級(jí)的性能優(yōu)化技巧可以在適當(dāng)?shù)臅r(shí)候讓程序盡可能的高效運(yùn)行! 22) 加速啟動(dòng)時(shí)間
能快速的啟動(dòng)程序非常重要,,特別是在用戶(hù)第一次啟動(dòng)程序時(shí),。第一映像對(duì)程序來(lái)說(shuō)非常重要! 讓程序盡量快速啟動(dòng)的方法就是盡量以異步方式執(zhí)行任務(wù),例如網(wǎng)絡(luò)請(qǐng)求,,數(shù)據(jù)訪(fǎng)問(wèn)或解析,。
另外,,避免使用臃腫的XIBs,因?yàn)閄IB的加載是在主線(xiàn)程中進(jìn)行的,。但是記住storyboard沒(méi)有這樣的問(wèn)題——所以如果可以的話(huà)就使用storyboard吧! 注意:在利用Xcode進(jìn)行調(diào)試時(shí),,watchdog不會(huì)運(yùn)行,所在設(shè)備中測(cè)試程序啟動(dòng)性能時(shí),,不要將設(shè)備連接到Xcode,。 23) 使用Autorelease Pool NSAutoreleasePool負(fù)責(zé)釋放一個(gè)代碼塊中的自動(dòng)釋放對(duì)象。一般都是由UIKit來(lái)創(chuàng)建的,。不過(guò)有些情況下需要手動(dòng)創(chuàng)建NSAutoreleasePool,。
例如,如果在代碼中創(chuàng)建了大量的臨時(shí)對(duì)象,,你將注意到內(nèi)存使用量在增加,,直到這些對(duì)象被釋放。問(wèn)題是只有當(dāng)UIKit耗盡了 autorelease pool,,這些對(duì)象才會(huì)被釋放,,也就是說(shuō)當(dāng)不再需要這些對(duì)象之后,這些對(duì)象還在內(nèi)存中占據(jù)著資源,。 不過(guò)這個(gè)問(wèn)題完全可以避免:在@autoreleasepool代碼塊中創(chuàng)建臨時(shí)對(duì)象,,如下代碼: NSArray *urls = <# An array of file URLs #>;for (NSURL *url in urls) { @autoreleasepool { NSError *error; NSString *fileContents = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error]; }}
當(dāng)每次迭代完之后,都會(huì)釋放所有的autorelease對(duì)象,。 關(guān)于NSAutoreleasePool的更多內(nèi)容可以閱讀蘋(píng)果的官方文檔,。 24) 緩存圖片 — 或者不緩存 iOS中從程序bundle中加載UIImage一般有兩種方法。第一種比較常見(jiàn):imageNamed,。第二種方法很少使用:imageWithContentsOfFile
為什么有兩種方法完成同樣的事情呢? imageNamed的優(yōu)點(diǎn)在于可以緩存已經(jīng)加載的圖片,。蘋(píng)果的文檔中有如下說(shuō)法: This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object. 這種方法會(huì)在系統(tǒng)緩存中根據(jù)指定的名字尋找圖片,如果找到了就返回,。如果沒(méi)有在緩存中找到圖片,,該方法會(huì)從指定的文件中加載圖片數(shù)據(jù),并將其緩存起來(lái),,然后再把結(jié)果返回,。 而imageWithContentsOfFile方法只是簡(jiǎn)單的加載圖片,并不會(huì)將圖片緩存起來(lái),。 這兩個(gè)方法的使用方法如下:
UIImage *img = [UIImage imageNamed:@"myImage"]; // caching// orUIImage *img = [UIImage imageWithContentsOfFile:@"myImage"]; // no caching 那么該如何選擇呢? 如果加載一張很大的圖片,,并且只使用一次,那么就不需要緩存這個(gè)圖片,。這種情況imageWithContentsOfFile比較合適——系統(tǒng)不會(huì)浪費(fèi)內(nèi)存來(lái)緩存圖片,。 然而,如果在程序中經(jīng)常需要重用的圖片,,那么最好是選擇imageNamed方法,。這種方法可以節(jié)省出每次都從磁盤(pán)加載圖片的時(shí)間,。 25) 盡量避免Date格式化 如果有許多日期需要使用NSDateFormatter,那么需要小心對(duì)待了,。如之前(重用花銷(xiāo)很大的對(duì)象)所提到的,,無(wú)論什么時(shí)候,,都應(yīng)該盡量重用NSDateFormatters,。 然而,如果你需要更快的速度,,那么應(yīng)該使用C來(lái)直接解析日期,,而不是NSDateFormatter。Sam Soffes寫(xiě)了一篇文章,,其中提供了一些解析ISO-8601格式日期字符的串代碼,。你只需要簡(jiǎn)單的調(diào)整一下其中的代碼就可以滿(mǎn)足自己特殊的需求了。
這聽(tīng)起來(lái)不錯(cuò)把——不過(guò)你相信這還有更好的一個(gè)辦法嗎?
如果你自己能控制處理日期的格式,,那么可以選擇 Unix timestamps,。Unix timestamps是一個(gè)簡(jiǎn)單的整數(shù),代表了從新紀(jì)元時(shí)間(epoch)開(kāi)始到現(xiàn)在已經(jīng)過(guò)了多少秒,,通常這個(gè)新紀(jì)元參考時(shí)間是00:00:00 UTC on 1 January 1970,。 你可以很容易的見(jiàn)這個(gè)時(shí)間戳轉(zhuǎn)換為NSDate,如下所示:
- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {
return [NSDate dateWithTimeIntervalSinc
上面這個(gè)方法比C函數(shù)還要快! 注意:許多網(wǎng)絡(luò)APIs返回的時(shí)間戳都是毫秒,,因此需要注意的是在將這個(gè)時(shí)間戳傳遞給dateFromUnixTimestamp之前需要除以1000,。
在開(kāi)發(fā)過(guò)程中,下面這些初級(jí)技巧需要時(shí)刻注意: 使用ARC進(jìn)行內(nèi)存管理 在適當(dāng)?shù)那闆r下使用reuseIdentifier 盡可能將View設(shè)置為不透明(Opaque) 避免臃腫的XIBs 不要阻塞主線(xiàn)程 讓圖片的大小跟UIImageView一樣 選擇正確的集合 使用GZIP壓縮 初級(jí)性能提升 本部分內(nèi)容介紹幾本的程序性能提升技巧,。其實(shí)所有級(jí)別的開(kāi)發(fā)者都能從中獲益,。 1) 使用ARC進(jìn)行內(nèi)存管理 ARC是在iOS 5中發(fā)布的,它解決了最常見(jiàn)的內(nèi)存泄露問(wèn)題——也是開(kāi)發(fā)者最容易健忘的,。 ARC的全稱(chēng)是“Automatic Reference Counting”——自動(dòng)引用計(jì)數(shù),,它會(huì)自動(dòng)的在代碼中做retain/release工作,開(kāi)發(fā)者不用再手動(dòng)處理,。 下面是創(chuàng)建一個(gè)View通用的一些代碼塊: UIView *view = [[UIView alloc] init];// ...[self.view addSubview:view];[view release]; 在上面代碼結(jié)束的地方很容易會(huì)忘記調(diào)用release,。不過(guò)當(dāng)使用ARC時(shí),ARC會(huì)在后臺(tái)自動(dòng)的幫你調(diào)用release,。 ARC除了能避免內(nèi)存泄露外,,還有助于程序性能的提升:當(dāng)程序中的對(duì)象不再需要的時(shí)候,ARC會(huì)自動(dòng)銷(xiāo)毀對(duì)象,。所以,,你應(yīng)該在工程中使用ARC。 下面是一些學(xué)習(xí)ARC很棒的一些資源: 蘋(píng)果的官方文檔 Matthijs Hollemans的初級(jí)ARC Tony Dahbura的如何在Cocos2D 2.X工程中使用ARC 如果你仍然不確定ARC帶來(lái)的好處,,那么看一些這篇文章:8個(gè)關(guān)于ARC的神話(huà)——這能夠讓你相信你應(yīng)該在工程中使用ARC! 值得注意的是,,ARC并不能避免所有的內(nèi)存泄露。使用ARC之后,,工程中可能還會(huì)有內(nèi)存泄露,,不過(guò)引起這些內(nèi)存泄露的主要原因是:block,retain循環(huán),,對(duì)CoreFoundation對(duì)象(通常是C結(jié)構(gòu))管理不善,,以及真的是代碼沒(méi)寫(xiě)好。 這里有一篇文章是介紹哪些問(wèn)題是ARC不能解決的 — 以及如何處理這些問(wèn)題,。 2) 在適當(dāng)?shù)那闆r下使用reuseIdentifier
在適當(dāng)?shù)那闆r使用reuseIdentifier
在iOS程序開(kāi)發(fā)中一個(gè)普遍性的錯(cuò)誤就是沒(méi)有正確的為UITableViewCells,、UICollectionViewCells和UITableViewHeaderFooterV 為了獲得最佳性能,,當(dāng)在tableView:cellForRowAtIndexPath:方法中返回cell時(shí),,table view的數(shù)據(jù)源一般會(huì)重用UITableViewCell對(duì)象。table view維護(hù)著UITableViewCell對(duì)象的一個(gè)隊(duì)列或者列表,,這些數(shù)據(jù)源已經(jīng)被標(biāo)記為重用了,。 如果沒(méi)有使用reuseIdentifier會(huì)發(fā)生什么? 如果你在程序中沒(méi)有使用reuseIdentifier,table view每次顯示一個(gè)row時(shí),,都會(huì)配置一個(gè)全新的cell,。這其實(shí)是一個(gè)非常消耗資源的操作,并且會(huì)影響程序中table view滾動(dòng)的效率,。 自iOS 6以來(lái),,你可能還希望header和footer views,以及UICollectionView的cell和supplementary views,。 為了使用reuseIdentifiers,,在table view請(qǐng)求一個(gè)新的cell時(shí),在數(shù)據(jù)源中調(diào)用下面的方法:
static NSString *CellIdentifier = @"Cell";UITableViewCell *cell =
[tableView dequeueReusableCellWithI
如果table
view維護(hù)的UITableViewCell隊(duì)列或列表中有可用的cell,,則從隊(duì)列從移除一個(gè)已經(jīng)存在的cell,,如果沒(méi)有的話(huà),就從之前注冊(cè)的nib文件或類(lèi)中創(chuàng)建一個(gè)新的cell,。如果沒(méi)有可以重用的cell,,并且沒(méi)有注冊(cè)nib文件或類(lèi),tableview的dequeueReusableCellWithI 3) 盡可能將View設(shè)置為不透明(Opaque)
盡量將view設(shè)置為Opaque 如果view是不透明的,,那么應(yīng)該將其opaque屬性設(shè)置為YES。 為什么要這樣做呢?這樣設(shè)置可以讓系統(tǒng)以最優(yōu)的方式來(lái)繪制view,。opaque屬性可以在Interface Builder或代碼中設(shè)置,。 蘋(píng)果的官方文檔對(duì)opaque屬性有如下解釋?zhuān)?/p> This property provides a hint to the drawing system as to how it should treat the view. If set to YES, the drawing system treats the view as fully opaque,, which allows the drawing system to optimize some drawing operations and improve performance. If set to NO,, the drawing system composites the view normally with other content. The default value of this property is YES. (opaque屬性提示繪制系統(tǒng)如何處理view,。如果opaque設(shè)置為YES,繪圖系統(tǒng)會(huì)將view看為完全不透明,,這樣繪圖系統(tǒng)就可以?xún)?yōu)化一些繪制操作以提升性能,。如果設(shè)置為NO,那么繪圖系統(tǒng)結(jié)合其它內(nèi)容來(lái)處理view,。默認(rèn)情況下,,這個(gè)屬性是YES。) 如果屏幕是靜止的,,那么這個(gè)opaque屬性的設(shè)置與否不是一個(gè)大問(wèn)題,。但是,如果view是嵌入到scroll view中的,,或者是復(fù)雜動(dòng)畫(huà)的一部分,不將設(shè)置這個(gè)屬性的話(huà)肯定會(huì)影響程序的性能! 可以通過(guò)模擬器的DebugColor Blended Layers選項(xiàng)來(lái)查看哪些view沒(méi)有設(shè)置為不透明,。為了程序的性能,,盡可能的將view設(shè)置為不透明! 4) 避免臃腫的XIBs
避免臃腫的XIBs 在iOS 5中開(kāi)始使用Storyboards,并且將替代XIBs,。不過(guò)在有些情況下XIBs仍然有用,。如果你的程序需要運(yùn)行在裝有iOS 5之前版本的設(shè)備上,或者要自定義可重用的view,,那么是避免不了要使用XIBs的,。 如果必須要使用XIBs的話(huà),盡量讓XIBs文件簡(jiǎn)單,。并且每個(gè)view controller對(duì)于一個(gè)XIB文件,,如果可以的話(huà),把一個(gè)view controller的view不同的層次單獨(dú)分到一個(gè)XIBs文件中,。 注意:當(dāng)把一個(gè)XIB文件加載到內(nèi)存時(shí),,XIB文件中的所有內(nèi)容都將被加載到內(nèi)存中,包括圖片,。如果有一個(gè)view還不立即使用的話(huà),,就會(huì)造成內(nèi)存的浪費(fèi)。而這在storyboard中是不會(huì)發(fā)生的,,因?yàn)閟toryboard還在需要的時(shí)候才實(shí)例化一個(gè)view controller,。 當(dāng)加載XIB時(shí),所有涉及到的圖片都將被緩存,,并且如果是開(kāi)發(fā)的程序是針對(duì)OS X的話(huà),,聲音文件也會(huì)被加載。蘋(píng)果的官方文檔這樣說(shuō): When you load a nib file that contains references to image or sound resources,, the nib-loading code reads the actual image or sound file into memory and and caches it. In OS X,, image and sound resources are stored in named caches so that you can access them later if needed. In iOS,, only image resources are stored in named caches. To access images, you use the imageNamed: method of NSImage or UIImage,, depending on your platform. (當(dāng)加載一個(gè)nib文件時(shí),,也會(huì)將nib文件涉及到的圖片或聲音資源加載到內(nèi)存中,nib-loading代碼會(huì)將實(shí)際的圖片或聲音文件讀取到內(nèi)存中,,并一直緩存著,。在OS X中,圖片和聲音資源都存儲(chǔ)在命名緩存中,,這樣之后如果需要的話(huà),,可以對(duì)其進(jìn)行訪(fǎng)問(wèn)。在iOS中,,只有圖片資源被存儲(chǔ)到命名緩存中,。要訪(fǎng)問(wèn)圖片的話(huà),使用NSImage或UIImage(根據(jù)不同的系統(tǒng))的imageNamed:方法即可,。) 顯然,,在使用storyboard時(shí)也會(huì)發(fā)生類(lèi)似的緩存操作;不過(guò)我沒(méi)有找到相關(guān)內(nèi)容的任何資料。如果你知道的話(huà),,可以告訴我哦! 想要學(xué)習(xí)storyboard的更多知識(shí)嗎?可以看看Matthijs Hollemans寫(xiě)的iOS 5中:初級(jí)Storyboard Part 1和Part2,。 5) 不要阻塞主線(xiàn)程
不要阻塞主線(xiàn)程 永遠(yuǎn)都不要在主線(xiàn)程做繁重的任務(wù)。因?yàn)閁IKit的任務(wù)都在主線(xiàn)程中進(jìn)行,,例如繪制,、觸摸管理和輸入響應(yīng)。 在主線(xiàn)程做所有任務(wù)的風(fēng)險(xiǎn)是:如果你的代碼阻塞了主線(xiàn)程,,那么程序?qū)⒊霈F(xiàn)反應(yīng)遲鈍,。這回招致用戶(hù)在App Store上對(duì)程序的差評(píng)! 在執(zhí)行I/O操作中,大多數(shù)情況下都會(huì)祖塞主線(xiàn)程,,這些操作需要從讀寫(xiě)外部資源,,例如磁盤(pán)或者網(wǎng)絡(luò)。 關(guān)于網(wǎng)絡(luò)操作可以使用NSURLConnection的如下方法,,以異步的方式來(lái)執(zhí)行: + (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*,, NSData*, NSError*))handler 或者使用第三方框架,,例如AFNetworking,。 如果你需要做一些其它類(lèi)型開(kāi)銷(xiāo)很大的操作(例如執(zhí)行一個(gè)時(shí)間密集型的計(jì)算或者對(duì)磁盤(pán)進(jìn)行讀寫(xiě)),那么就使用GCD(Grand Central Dispatch),,或NSOperations 和 NSOperationQueues,。 下面的代碼是使用GCD的一個(gè)模板: dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // switch to a background thread and perform your expensive operation dispatch_async(dispatch_get_main_queue(),, ^{ // switch back to the main thread to update your UI });}); 如上代碼,,為什么在第一個(gè)dispatch_async里面還嵌套了一個(gè)dispatch_async呢?這是因?yàn)殛P(guān)于UIKit相關(guān)的代碼需要在主線(xiàn)程里面執(zhí)行。 對(duì)NSOperation和GCD感到好奇嗎?可以看看Ray Wenderlich中的教程:iOS中多線(xiàn)程和GCD—初級(jí),,以及Soheil Azarpour的如何使用NSOperations和NSOperationQueues教程,。 6) 讓圖片的大小跟UIImageView一樣
確保圖片和UIImageView大小一致 如果需要將程序bundle中的圖片顯示到UIImageView中,請(qǐng)確保圖片和UIImageView的大小是一樣的,。因?yàn)閳D片的縮放非常耗費(fèi)資源,,特別是將UIImageView嵌入到UIScrollView中。 如果是從遠(yuǎn)程服務(wù)中下載圖片,,有時(shí)候你控制不了圖片的尺寸,,或者在下載之前無(wú)法在服務(wù)器上進(jìn)行圖片的縮放。這種情況,,當(dāng)圖片下載完之后,,你可以手動(dòng)進(jìn)行圖片的縮放——做好是在后臺(tái)線(xiàn)程中!——然后再在UIImageView中使用縮放過(guò)的圖片。 7) 選擇正確的集合
選擇正確的集合 學(xué)習(xí)使用最適合的類(lèi)或?qū)ο笫蔷帉?xiě)高效代碼的基礎(chǔ),。特別是在處理集合數(shù)據(jù)時(shí),,尤為重要。 蘋(píng)果的官網(wǎng)上有一篇文章:集合編程主題(Collections Programming Topics)——詳細(xì)的介紹了在集合數(shù)據(jù)中可以使用的類(lèi),,以及什么情況下使用哪個(gè)類(lèi)。在使用集合時(shí),,每個(gè)開(kāi)發(fā)者都應(yīng)該閱讀一下這個(gè)文檔,。 太長(zhǎng),不想閱讀(TLDR)?下面是常見(jiàn)集合類(lèi)型的一個(gè)簡(jiǎn)介: 數(shù)組:是一個(gè)值按順序排列的一個(gè)列表,。根據(jù)索引可以快速查找,,不過(guò)根據(jù)值進(jìn)行查找就比較慢,另外插入和刪除也比較慢,。 字典: 存儲(chǔ)鍵/值對(duì),。根據(jù)鍵可以快速查找。 Sets: 是一個(gè)值無(wú)序排列的列表,,根據(jù)值可以快速查找,,另外插入和刪除也比較快。 8) 使用GZIP壓縮
使用GZIP壓縮 越來(lái)越多的程序依賴(lài)于外部數(shù)據(jù),,這些數(shù)據(jù)一般來(lái)自遠(yuǎn)程服務(wù)器或者其它的外部APIs,。有時(shí)候你需要開(kāi)發(fā)一個(gè)程序來(lái)下載一些數(shù)據(jù),這些數(shù)據(jù)可以是XML,,JSON,,HTML或者其它一些文本格式。 問(wèn)題是在移動(dòng)設(shè)備上的網(wǎng)絡(luò)是不確定的,。用戶(hù)的設(shè)備可能在EDGE網(wǎng)絡(luò)一分鐘,,然后接著又在3G網(wǎng)絡(luò)中,。不管在什么情況下,都不要讓用戶(hù)等待,。 有一個(gè)可以?xún)?yōu)化的選擇:使用GZIP對(duì)網(wǎng)絡(luò)傳輸中的數(shù)據(jù)進(jìn)行壓縮,,這樣可以減小文件的大小,并加快下載的速度,。壓縮對(duì)于文本數(shù)據(jù)特別有用,,因?yàn)槲谋揪哂泻芨叩膲嚎s比。 iOS中,,如果使用NSURLConnection,,那么默認(rèn)情況下已經(jīng)支持GZIP壓縮了,并且基于NSURLConnection的框架頁(yè)支持GZIP壓縮,,如AFNetworking,。甚至有些云服務(wù)提供商已經(jīng)提供發(fā)送經(jīng)壓縮過(guò)的響應(yīng)內(nèi)容,例如 Google App Engine,。 這里有一篇關(guān)于GZIP壓縮很好的文章,,介紹了如何在Apache活I(lǐng)IS服務(wù)器中開(kāi)啟支持GZIP壓縮。
|
|
來(lái)自: 嘆落花 > 《內(nèi)存警告》