1. 用ARC管理內存
2. 在正確的地方使用reuseIdentifier
3. 盡可能使Views透明
4. 避免龐大的XIB
5. 不要block主線程
6. 在Image Views中調整圖片大小
7. 選擇正確的Collection
8. 打開gzip壓縮
9. 重用和延遲加載Views
10. Cache, Cache, 還是Cache肄方!
11. 權衡渲染方法
12. 處理內存警告
13. 重用大開銷的對象
14. 使用Sprite Sheets
15. 避免反復處理數(shù)據(jù)
16. 選擇正確的數(shù)據(jù)格式
17. 正確地設定Background Images
18. 減少使用Web特性
19. 設定Shadow Path
20. 優(yōu)化你的Table View
21. 選擇正確的數(shù)據(jù)存儲選項
22. 加速啟動時間
23. 使用Autorelease Pool
24. 選擇是否緩存圖片
25. 盡量避免日期格式轉換
26. NSTimer的使用
27. block的使用
1.用ARC管理內存
-
ARC(Automatic Reference Counting, 自動引用計數(shù))和iOS5一起發(fā)布,它避免了最常見的也就是經(jīng)常是由于我們忘記釋放內存所造成的內存泄露。它自動為你管理retain和release的過程,所以你就不必去手動干預了。
下面是你會經(jīng)常用來去創(chuàng)建一個View的代碼段:UIView *view = [[UIView alloc] init]; // ... [self.view addSubview:view]; [view release];
忘掉代碼段結尾的release簡直像記得吃飯一樣簡單缀去。而ARC會自動在底層為你做這些工作。除了幫你避免內存泄露,ARC還可以幫你提高性能欢搜,它能保證釋放掉不再需要的對象的內存。這都啥年代了谴轮,你應該在你的所有項目里使用ARC!
2.在正確的地方使用 reuseIdentifier
一個開發(fā)中常見的錯誤就是沒有給UITableViewCells炒瘟, UICollectionViewCells,甚至是UITableViewHeaderFooterViews設置正確的reuseIdentifier第步。
為了性能最優(yōu)化疮装,table view用
tableView:cellForRowAtIndexPath:
為rows分配cells的時候,它的數(shù)據(jù)應該重用自UITableViewCell粘都。 一個table view維持一個隊列的數(shù)據(jù)可重用的UITableViewCell對象廓推。不使用reuseIdentifier的話,每顯示一行table view就不得不設置全新的cell翩隧。這對性能的影響可是相當大的樊展,尤其會使app的滾動體驗大打折扣。自iOS6起,除了UICollectionView的cells和補充views专缠,你也應該在header和footer views中使用reuseIdentifiers
3.盡量把views設置為完全不透明
如果你有透明的Views你應該設置它們的opaque(不透明)屬性為YES雷酪。例如一個黑色半透明的可以設置為一個灰色不透明的View替代.原因是這會使系統(tǒng)用一個最優(yōu)的方式渲染這些views。這個簡單的屬性在IB或者代碼里都可以設定涝婉。
Apple的文檔對于為圖片設置透明屬性的描述是:
(opaque)這個屬性給渲染系統(tǒng)提供了一個如何處理這個view的提示哥力。如果設為YES, 渲染系統(tǒng)就認為這個view是完全不透明的墩弯,這使得渲染系統(tǒng)優(yōu)化一些渲染過程和提高性能吩跋。如果設置為NO,渲染系統(tǒng)正常地和其它內容組成這個View最住。默認值是YES钞澳。在相對比較靜止的畫面中,設置這個屬性不會有太大影響涨缚。然而當這個view嵌在scroll view里邊轧粟,或者是一個復雜動畫的一部分,不設置這個屬性的話會在很大程度上影響app的性能脓魏。
-
換種說法兰吟,大家可能更好理解:
只要一個視圖的不透明度小于1,就會導致blending.blending操作在iOS的圖形處理器(GPU)中完成的,blending主要指的是混合像素顏色的計算。舉個例子,我們把兩個圖層疊加在一起,如果第一個圖層的有透明效果,則最終像素的顏色計算需要將第二個圖層也考慮進來茂翔。這一過程即為Blending混蔼。
為什么Blending會導致性能的損失?
原因是很直觀的,如果一個圖層是完全不透明的,則系統(tǒng)直接顯示該圖層的顏色即可珊燎。而如果圖層是帶透明效果的,則會引入更多的計算,因為需要把下面的圖層也包括進來,進行混合后顏色的計算惭嚣。
4. 避免過于龐大的XIB
- iOS5中加入的Storyboards(分鏡)正在快速取代XIB。然而XIB在一些場景中仍然很有用悔政。比如你的app需要適應iOS5之前的設備晚吞,或者你有一個自定義的可重用的view,你就不可避免地要用到他們。
- 如果你不得不XIB的話谋国,使他們盡量簡單槽地。嘗試為每個Controller配置一個單獨的XIB,盡可能把一個View Controller的view層次結構分散到單獨的XIB中去芦瘾。
需要注意的是捌蚊,當你加載一個XIB的時候所有內容都被放在了內存里,包括任何圖片近弟。如果有一個不會即刻用到的view缅糟,你這就是在浪費寶貴的內存資源了。Storyboards就是另一碼事兒了藐吮,storyboard僅在需要時實例化一個view controller. - 當你加載一個引用了圖片或者聲音資源的nib時溺拱,nib加載代碼會把圖片和聲音文件寫進內存逃贝。在OS X中,圖片和聲音資源被緩存在named cache中以便將來用到時獲取迫摔。在iOS中沐扳,僅圖片資源會被存進named caches。取決于你所在的平臺句占,使用NSImage 或UIImage 的
imageNamed:
方法來獲取圖片資源沪摄。
5. 不要阻塞主線程
永遠不要使主線程承擔過多。因為UIKit在主線程上做所有工作纱烘,渲染杨拐,管理觸摸反應,回應輸入等都需要在它上面完成擂啥。一直使用主線程的風險就是如果你的代碼真的block了主線程哄陶,你的app會失去反應
-
大部分阻礙主進程的情形是你的app在做一些牽涉到讀寫外部資源的I/O操作,比如存儲或者網(wǎng)絡哺壶∥荻郑或者使用像 AFNetworking這樣的框架來異步地做這些操作。
如果你需要做其它類型的需要耗費巨大資源的操作(比如時間敏感的計算或者存儲讀寫)那就用 Grand Central Dispatch山宾,或者 NSOperation 和 NSOperationQueues.
你可以使用NSURLConnection
異步地做網(wǎng)絡操作:+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler
6. 在Image Views中調整圖片大小
- 如果要在
UIImageView
中顯示一個來自bundle的圖片至扰,你應保證圖片的大小和UIImageView的大小相同。在運行中縮放圖片是很耗費資源的资锰,特別是UIImageView
嵌套在UIScrollView
中的情況下敢课。 - 如果圖片是從遠端服務加載的你不能控制圖片大小,比如在下載前調整到合適大小的話绷杜,你可以在下載完成后直秆,最好是用background thread,縮放一次鞭盟,然后在UIImageView中使用縮放后的圖片切厘。
7. 選擇正確的Collection
學會選擇對業(yè)務場景最合適的類或者對象是寫出能效高的代碼的基礎。當處理collections時這句話尤其正確懊缺。
Apple有一個 Collections Programming Topics 的文檔詳盡介紹了可用的classes間的差別和你該在哪些場景中使用它們。這對于任何使用collections的人來說是一個必讀的文檔培他。
呵呵鹃两,我就知道你因為太長沒看…這是一些常見collection的總結:
- Arrays: 有序的一組值。使用index來lookup很快舀凛,使用value lookup很慢俊扳, 插入/刪除很慢。
- Dictionaries: 存儲鍵值對猛遍。 用鍵來查找比較快馋记。
- Sets: 無序的一組值号坡。用值來查找很快,插入/刪除很快梯醒。
8. 打開gzip壓縮
- 大量app依賴于遠端資源和第三方API宽堆,你可能會開發(fā)一個需要從遠端下載XML, JSON, HTML或者其它格式的app。
- 問題是我們的目標是移動設備茸习,因此你就不能指望網(wǎng)絡狀況有多好畜隶。一個用戶現(xiàn)在還在edge網(wǎng)絡,下一分鐘可能就切換到了3G号胚。不論什么場景籽慢,你肯定不想讓你的用戶等太長時間。
- 減小文檔的一個方式就是在服務端和你的app中打開gzip猫胁。這對于文字這種能有更高壓縮率的數(shù)據(jù)來說會有更顯著的效用箱亿。好消息是,iOS已經(jīng)在NSURLConnection中默認支持了gzip壓縮弃秆,當然AFNetworking這些基于它的框架亦然届惋。像Google App Engine這些云服務提供者也已經(jīng)支持了壓縮輸出。
9. 重用和延遲加載(lazy load) Views
更多的view意味著更多的渲染驾茴,也就是更多的CPU和內存消耗盼樟,對于那種嵌套了很多view在UIScrollView里邊的app更是如此。
這里我們用到的技巧就是模仿UITableView
和UICollectionView
的操作: 不要一次創(chuàng)建所有的subview锈至,而是當需要時才創(chuàng)建晨缴,當它們完成了使命,把他們放進一個可重用的隊列中峡捡。
這樣的話你就只需要在滾動發(fā)生時創(chuàng)建你的views击碗,避免了不劃算的內存分配。
創(chuàng)建views的能效問題也適用于你app的其它方面们拙。想象一下一個用戶點擊一個按鈕的時候需要呈現(xiàn)一個view的場景稍途。有兩種實現(xiàn)方法:
- 創(chuàng)建并隱藏這個view當這個screen加載的時候,當需要時顯示它砚婆;
- 當需要時才創(chuàng)建并展示械拍。
每個方案都有其優(yōu)缺點。
用第一種方案的話因為你需要一開始就創(chuàng)建一個view并保持它直到不再使用装盯,這就會更加消耗內存坷虑。然而這也會使你的app操作更敏感因為當用戶點擊按鈕的時候它只需要改變一下這個view的可見性。
第二種方案則相反-消耗更少內存,但是會在點擊按鈕的時候比第一種稍顯卡頓。
10. Cache, Cache, 還是Cache!
一個極好的原則就是浩销,緩存所需要的,也就是那些不大可能改變但是需要經(jīng)常讀取的東西芹敌。
我們能緩存些什么呢痊远?一些選項是,遠端服務器的響應氏捞,圖片碧聪,甚至計算結果,比如UITableView的行高幌衣。
NSURLConnection默認會緩存資源在內存或者存儲中根據(jù)它所加載的HTTP Headers矾削。你甚至可以手動創(chuàng)建一個NSURLRequest然后使它只加載緩存的值。
下面是一個可用的代碼段豁护,你可以可以用它去為一個基本不會改變的圖片創(chuàng)建一個NSURLRequest并緩存它:
+ (NSMutableURLRequest *)imageRequestWithURL:(NSURL *)url {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;// this will make sure the request always returns the cached image
request.HTTPShouldHandleCookies = NO;
request.HTTPShouldUsePipelining = YES;
[request addValue:@"image/*"forHTTPHeaderField:@"Accept"];
returnrequest;
}
注意你可以通過 NSURLConnection 獲取一個URL request哼凯, AFNetworking也一樣的。這樣你就不必為采用這條tip而改變所有的networking代碼了楚里。
如果想了解更多關于HTTP caching, NSURLCache, NSURLConnection的相關知識断部,可以讀下這篇文章()
如果你需要緩存其它不是HTTP Request的東西,你可以用NSCache班缎。
NSCache和NSDictionary類似蝴光,不同的是系統(tǒng)回收內存的時候它會自動刪掉它的內容。
11. 權衡渲染方法
在iOS中可以有很多方法做出漂亮的按鈕达址。你可以用整幅的圖片蔑祟,可調大小的圖片,uozhe可以用CALayer沉唠, CoreGraphics甚至OpenGL來畫它們疆虚。
當然每個不同的解決方法都有不同的復雜程度和相應的性能。有一篇Apple UIKit team中的一員Andy Matuschak推薦過的很棒的關于graphic性能的帖子很值得一讀满葛。
- 簡單來說径簿,就是用事先渲染好的圖片更快一些,因為如此一來iOS就免去了創(chuàng)建一個圖片再畫東西上去然后顯示在屏幕上的程序嘀韧。問題是你需要把所有你需要用到的圖片放到app的bundle里面篇亭,這樣就增加了體積 – 這就是使用可變大小的圖片更好的地方了: 你可以省去一些不必要的空間,也不需要再為不同的元素(比如按鈕)來做不同的圖锄贷。
然而译蒂,使用圖片也意味著你失去了使用代碼調整圖片的機動性,你需要一遍又一遍不斷地重做他們谊却,這樣就很浪費時間了蹂随,而且你如果要做一個動畫效果,雖然每幅圖只是一些細節(jié)的變化你就需要很多的圖片造成bundle大小的不斷增大因惭。 - 總得來說,你需要權衡一下利弊绩衷,到底是要性能能還是要bundle保持合適的大小蹦魔。
12. 處理內存警告
一旦系統(tǒng)內存過低激率,iOS會通知所有運行中app。在官方文檔中是這樣記述:
如果你的app收到了內存警告勿决,它就需要盡可能釋放更多的內存乒躺。最佳方式是移除對緩存,圖片object和其他一些可以重創(chuàng)建的objects的strong references.
幸運的是低缩,UIKit提供了幾種收集低內存警告的方法:
- 在app delegate中使用
applicationDidReceiveMemoryWarning:
的方法 - 在你的自定義UIViewController的子類(subclass)中覆蓋
didReceiveMemoryWarning
- 注冊并接收 UIApplicationDidReceiveMemoryWarningNotification 的通知
一旦收到這類通知嘉冒,你就需要釋放任何不必要的內存使用。
例如咆繁,UIViewController的默認行為是移除一些不可見的view讳推, 它的一些子類則可以補充這個方法,刪掉一些額外的數(shù)據(jù)結構玩般。一個有圖片緩存的app可以移除不在屏幕上顯示的圖片银觅。
這樣對內存警報的處理是很必要的,若不重視坏为,你的app就可能被系統(tǒng)殺掉究驴。
然而,當你一定要確認你所選擇的object是可以被重現(xiàn)創(chuàng)建的來釋放內存匀伏。一定要在開發(fā)中用模擬器中的內存提醒模擬去測試一下洒忧。
13. 重用大開銷對象
一些objects的初始化很慢,比如NSDateFormatter和NSCalendar够颠。然而熙侍,你又不可避免地需要使用它們,比如從JSON或者XML中解析數(shù)據(jù)摧找。
想要避免使用這個對象的瓶頸你就需要重用他們核行,可以通過添加屬性到你的class里或者創(chuàng)建靜態(tài)變量來實現(xiàn)。
注意如果你要選擇第二種方法蹬耘,對象會在你的app運行時一直存在于內存中芝雪,和單例(singleton)很相似。
下面的代碼說明了使用一個屬性來延遲加載一個date formatter. 第一次調用時它會創(chuàng)建一個新的實例综苔,以后的調用則將返回已經(jīng)創(chuàng)建的實例:
// in your .h or inside a class extension
@property (nonatomic, strong) NSDateFormatter *formatter;
// inside the implementation (.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)建新的一樣慢的!所以如果你的app需要經(jīng)常進行日期格式處理的話如筛,你會從這個方法中得到不小的性能提升堡牡。
14. 使用Sprite Sheets
你是一個游戲開發(fā)者嗎,那么Sprite sheets一定是一個你的最好的朋友了杨刨。Sprite sheet可以讓渲染速度加快晤柄,甚至比標準的屏幕渲染方法節(jié)省內存。
我們有兩個很好的關于Sprite的教程:
- How To Use Animations and Sprite Sheets in Cocos2D
- How to Create and Optimize Sprite Sheets in Cocos2D with Texture Packer and Pixel Formats
第二個教程涵蓋了可能在很大程度上影響你游戲性能的pixel格式的細節(jié)妖胀。
如果你對于spirte sheet還不是很熟悉芥颈,可以看下這兩個(youtube)視頻SpriteSheets – The Movie, Part 1 和Part 2惠勒。視頻的作者是創(chuàng)建Sprite sheet很流行的工具之一Texture Packer的作者Andreas L?w。
除了使用Sprite sheets爬坑,其它寫在這里的建議當然也可以用于游戲開發(fā)中纠屋。比如你需要很多的Sprite sheets,像敵人盾计,導彈之類的動作類必備元素售担,你可以重用這些sprites而不用每次都要重新創(chuàng)建。
15. 避免反復處理數(shù)據(jù)
許多應用需要從服務器加載功能所需的常為JSON或者XML格式的數(shù)據(jù)署辉。在服務器端和客戶端使用相同的數(shù)據(jù)結構很重要族铆。在內存中操作數(shù)據(jù)使它們滿足你的數(shù)據(jù)結構是開銷很大的。
比如你需要數(shù)據(jù)來展示一個table view,最好直接從服務器取array結構的數(shù)據(jù)以避免額外的中間數(shù)據(jù)結構改變涨薪。
類似的骑素,如果需要從特定key中取數(shù)據(jù),那么就使用鍵值對的dictionary刚夺。
16. 選擇正確的數(shù)據(jù)格式
從app和網(wǎng)絡服務間傳輸數(shù)據(jù)有很多方案献丑,最常見的就是JSON和XML。你需要選擇對你的app來說最合適的一個侠姑。
- 解析JSON會比XML更快一些创橄,JSON也通常更小更便于傳輸。從iOS5起有了官方內建的JSON deserialization 就更加方便使用了莽红。
- 但是XML也有XML的好處妥畏,比如使用SAX 來解析XML就像解析本地文件一樣,你不需像解析json一樣等到整個文檔下載完成才開始解析安吁。當你處理很大的數(shù)據(jù)的時候就會極大地減低內存消耗和增加性能醉蚁。
17. 正確設定背景圖片
在View里放背景圖片就像很多其它iOS編程一樣有很多方法:
- 使用UIColor的 colorWithPatternImage來設置背景色;
- 在view中添加一個UIImageView作為一個子View鬼店。
如果你使用全畫幅的背景圖网棍,你就必須使用UIImageView因為UIColor的colorWithPatternImage是用來創(chuàng)建小的重復的圖片作為背景的。這種情形下使用UIImageView可以節(jié)約不少的內存:
// You could also achieve the same result in Interface Builder
UIImageView *backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background"]];
[self.view addSubview:backgroundView];
如果你用小圖平鋪來創(chuàng)建背景妇智,你就需要用UIColor的colorWithPatternImage來做了滥玷,它會更快地渲染也不會花費很多內存:
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background"]];
18. 減少使用Web特性
UIWebView很有用,用它來展示網(wǎng)頁內容或者創(chuàng)建UIKit很難做到的動畫效果是很簡單的一件事巍棱。
但是你可能有注意到UIWebView并不像驅動Safari的那么快惑畴。這是由于以JIT compilation 為特色的Webkit的Nitro Engine的限制。
所以想要更高的性能你就要調整下你的HTML了航徙。第一件要做的事就是盡可能移除不必要的javascript如贷,避免使用過大的框架。能只用原生js就更好了。
另外杠袱,盡可能異步加載例如用戶行為統(tǒng)計script這種不影響頁面表達的javascript泻红。
最后,永遠要注意你使用的圖片霞掺,保證圖片的符合你使用的大小。使用Sprite sheet提高加載速度和節(jié)約內存讹躯。
更多相關信息可以看下 WWDC 2012 session #601 – Optimizing Web Content in UIWebViews and Websites on iOS
19. 設定Shadow Path
如何在一個View或者一個layer上加一個shadow呢菩彬,QuartzCore框架是很多開發(fā)者的選擇:
#import <QuartzCore/QuartzCore.h>
// Somewhere later ...
UIView *view = [[UIView alloc] init];
// Setup the shadow ...
view.layer.shadowOffset = CGSizeMake(-1.0f, 1.0f);
view.layer.shadowRadius = 5.0f;
view.layer.shadowOpacity = 0.6;
看起來很簡單,對吧潮梯。
可是骗灶,壞消息是使用這個方法也有它的問題… Core Animation不得不先在后臺得出你的圖形并加好陰影然后才渲染,這開銷是很大的秉馏。
使用shadowPath的話就避免了這個問題:
view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath];
使用shadow path的話iOS就不必每次都計算如何渲染耙旦,它使用一個預先計算好的路徑。但問題是自己計算path的話可能在某些View中比較困難萝究,且每當view的frame變化的時候你都需要去update shadow path.
想了解更多可以看看Mark Pospesel的這篇免都。
20. 優(yōu)化Table View
Table view需要有很好的滾動性能,不然用戶會在滾動過程中發(fā)現(xiàn)動畫的瑕疵帆竹。
為了保證table view平滑滾動绕娘,確保你采取了以下的措施:
- 正確使用
reuseIdentifier
來重用cells - 盡量使所有的view opaque,包括cell自身
- 避免漸變栽连,圖片縮放险领,后臺選人
- 緩存行高
- 如果cell內現(xiàn)實的內容來自web,使用異步加載秒紧,緩存請求結果
- 使用
shadowPath
來畫陰影 - 減少subviews的數(shù)量
- 盡量不適用
cellForRowAtIndexPath:
绢陌,如果你需要用到它,只用一次然后緩存結果 - 使用正確的數(shù)據(jù)結構來存儲數(shù)據(jù)
- 盡量使用
rowHeight
,sectionFooterHeight
和sectionHeaderHeight
來設定固定的高熔恢,不要請求delegate
21. 選擇正確的數(shù)據(jù)存儲選項
當做本地數(shù)據(jù)存儲時你會怎么做脐湾?
你有很多選擇,比如:
- 使用
NSUerDefaults
- 使用XML, JSON, 或者 plist
- 使用NSCoding存檔
- 使用類似SQLite的本地SQL數(shù)據(jù)庫
- 使用 Core Data
NSUserDefaults的問題是什么绩聘?雖然它很nice也很便捷沥割,但是它只適用于小數(shù)據(jù),比如一些簡單的布爾型的設置選項凿菩,再大點你就要考慮其它方式了
XML這種結構化檔案呢机杜?總體來說,你需要讀取整個文件到內存里去解析衅谷,這樣是很不經(jīng)濟的椒拗。使用SAX又是一個很麻煩的事情。
NSCoding?不幸的是蚀苛,它也需要讀寫文件在验,所以也有以上問題。
當存儲大塊數(shù)據(jù)時,以上的方法都不適用. 在這種應用場景下堵未,使用SQLite 或者 Core Data比較好腋舌。使用這些技術你用特定的查詢語句就能只加載你需要的對象。
在性能層面來講渗蟹,SQLite和Core Data是很相似的块饺。他們的不同在于具體使用方法。Core Data代表一個對象的graph model雌芽,但SQLite就是一個DBMS授艰。Apple在一般情況下建議使用Core Data,但是如果你有理由不使用它世落,那么就去使用更加底層的SQLite吧淮腾。
22. 加速啟動時間
快速打開app是很重要的,特別是用戶第一次打開它時屉佳,對app來講谷朝,第一印象太太太重要了。
- 你能做的就是使它盡可能做更多的異步任務忘古,比如加載遠端或者數(shù)據(jù)庫數(shù)據(jù)徘禁,解析數(shù)據(jù)。
還是那句話髓堪,避免過于龐大的XIB送朱,因為他們是在主線程上加載的。所以盡量使用沒有這個問題的Storyboards吧干旁!
注意驶沼,用Xcode debug時watchdog并不運行,一定要把設備從Xcode斷開來測試啟動速度
23. 使用Autorelease Pool
NSAutoreleasePool
負責釋放block中的autoreleased objects争群。一般情況下它會自動被UIKit調用回怜。但是有些狀況下你也需要手動去創(chuàng)建它。
假如你創(chuàng)建很多臨時對象换薄,你會發(fā)現(xiàn)內存一直在減少直到這些對象被release的時候玉雾。這是因為只有當UIKit用光了autorelease pool的時候memory才會被釋放。
好消息是你可以在你自己的@autoreleasepool里創(chuàng)建臨時的對象來避免這個行為:
NSArray *urls = <# An array of file URLs #>;
for(NSURL *url in urls) {
@autoreleasepool {
NSError *error;
NSString *fileContents = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding error:&error];
/* Process the string, creating and autoreleasing more objects. */
}
}
這段代碼在每次遍歷后釋放所有autorelease對象
更多關于NSAutoreleasePool請參考官方文檔轻要。
24. 選擇是否緩存圖片
常見的從bundle中加載圖片的方式有兩種复旬,一個是用imageNamed
,二是用imageWithContentsOfFile
冲泥,第一種比較常見一點驹碍。
既然有兩種類似的方法來實現(xiàn)相同的目的壁涎,那么他們之間的差別是什么呢?
imageNamed
的優(yōu)點是當加載時會緩存圖片志秃。imageNamed
的文檔中這么說:
這個方法用一個指定的名字在系統(tǒng)緩存中查找并返回一個圖片對象如果它存在的話怔球。如果緩存中沒有找到相應的圖片,這個方法從指定的文檔中加載然后緩存并返回這個對象浮还。
相反的竟坛,imageWithContentsOfFile
僅加載圖片。
下面的代碼說明了這兩種方法的用法:
UIImage *img = [UIImage imageNamed:@"myImage"];// caching
// or
UIImage *img = [UIImage imageWithContentsOfFile:@"myImage"];// no caching
那么我們應該如何選擇呢钧舌?
如果你要加載一個大圖片而且是一次性使用流码,那么就沒必要緩存這個圖片,用imageWithContentsOfFile
足矣延刘,這樣不會浪費內存來緩存它。
然而六敬,在圖片反復重用的情況下imageNamed
是一個好得多的選擇碘赖。
25. 避免日期格式轉換
如果你要用NSDateFormatter
來處理很多日期格式,應該小心以待外构。就像先前提到的普泡,任何時候重用NSDateFormatters
都是一個好的實踐。
然而审编,如果你需要更多速度撼班,那么直接用C是一個好的方案。Sam Soffes有一個不錯的帖子(http://soff.es/how-to-drastically-improve-your-app-with-an-afternoon-and-instruments)里面有一些可以用來解析ISO-8601日期字符串的代碼垒酬,簡單重寫一下就可以拿來用了砰嘁。
嗯,直接用C來搞勘究,看起來不錯了矮湘,但是你相信嗎,我們還有更好的方案口糕!
如果你可以控制你所處理的日期格式缅阳,盡量選擇Unix時間戳。你可以方便地從時間戳轉換到NSDate:
- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {
return[NSDate dateWithTimeIntervalSince1970:timestamp];
}
這樣會比用C來解析日期字符串還快景描!
需要注意的是十办,許多web API會以微秒的形式返回時間戳,因為這種格式在javascript中更方便使用超棺。記住用dateFromUnixTimestamp
之前除以1000就好了向族。
26. NSTimer的使用
在 Controller B 中有一個 NSTimer
@property (strong, nonatomic) NSTimer *timer
你創(chuàng)建了它,并掛載到 main runloop
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self selector:@selector(timerAction:) userInfo:nil repeats:``true``];
然后退出 Controller B 的時候说搅,忘記關掉 timer 了
Controller B 將不會釋放炸枣,B 與 timer 循環(huán)引用。因為創(chuàng)建 timer 的時候把 self 直接寫進去了。
使用weakSelf一樣有強引用問題:
__weak ``typeof``(self) weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
target:weakSelf selector:@selector(timerAction:) userInfo:nil repeats:``true``];
解決方案:
1)在viewDidDisappear的時候釋放定時器
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
if (self.timer) {
[self.timer invalidate];
self.timer = nil;
}
}
2)使用以下方法代替:
- (void)loop {
[self doSomething];
......
// 休息 time 秒适肠,再調 loop霍衫,實現(xiàn)定時調用
[NSThread sleepForTimeInterval:time];
dispatch_async(self.runQueue, ^{
[weakSelf loop];
});
}
27. block的使用
Block 里不能寫 self,需要使用weakSelf
__weak ``typeof``(self) weakSelf = self;
dispatch_async(self.runQueue, ^{
[weakSelf loop];
});