優化TableViewhtml
異步繪製!若是 cell 比較複雜,可使用!ios
self.layer.drawsAsynchronously = YES;git
柵格化,美工的術語:將 cell 中的全部內容,生成一張獨立的圖像github
在屏幕滾動時,只顯示圖像web
self.layer.shouldRasterize = YES;sql
柵格化,必須指定分辨率,不然默認使用 * 1,生成圖像!數據庫
self.layer.rasterizationScale = [UIScreen mainScreen].scale;編程
在咱們使用 UITableView 和 UICollectionView 時咱們一般會遇到「複用 Cell」這個提法,所謂「複用 Cell」就是指當須要展現的數據條目較多時,只建立較少數量的 Cell 對象(通常是屏幕可顯示的 Cell 數再加一)並經過複用它們的方式來展現數據的機制。這種機制不會爲每一條數據都建立一個 Cell,因此能夠節省內存,提高程序的效率和交互流暢性。
從 iOS 6 之後,咱們在 UITableView 和 UICollectionView 中不光能夠複用 Cell,還能夠複用各個 Section 的 Header 和 Footer。
在 UITableView 作複用的時候,會用到的 API:數組
// 複用 Cell: - [UITableView dequeueReusableCellWithIdentifier:]; - [UITableView registerNib:forCellReuseIdentifier:]; - [UITableView registerClass:forCellReuseIdentifier:]; - [UITableView dequeueReusableCellWithIdentifier:forIndexPath:];// 複用 Section 的 Header/Footer: - [UITableView registerNib:forHeaderFooterViewReuseIdentifier:]; - [UITableView registerClass:forHeaderFooterViewReuseIdentifier:]; - [UITableView dequeueReusableHeaderFooterViewWithIdentifier:];
複用機制是一個很好的機制,可是不正確的使用卻會給咱們的程序帶來不少問題。下面拿 UITableView 複用 Cell 來舉例:緩存
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"UITableViewCell"; UITableViewCell *cell = nil; cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; // 偶數行 Cell 的 textLabel 的文字顏色爲紅色。 if (indexPath.row % 2 == 0) { [cell.textLabel setTextColor:[UIColor redColor]]; } } cell.textLabel.text = @"Title"; // 偶數行 Cell 的 detailTextLabel 顯示 Detail 文字。 if (indexPath.row % 2 == 0) { cell.detailTextLabel.text = @"Detail"; } return cell; }
咱們原本是但願只有偶數行的 textLabel 的文字顏色爲紅色,而且顯示 Detail 文字,可是當你滑動 TableView 的時候發現不對了,有些奇數行的 textLabel 的文字顏色爲紅色,並且還顯示了 Detail 文字,很奇怪。其實形成這個問題的緣由就是「複用」,當一個 Cell 被拿來複用時,它全部被設置的屬性(包括樣式和內容)都會被拿來複用,若是恰好某一個的 Cell 你沒有顯式地設置它的屬性,那麼它這些屬性就直接複用別的 Cell 的了。就如上面的代碼中,咱們並無顯式地設置奇數行的 Cell 的 textLabel 的文字顏色以及 detailTextLabel 的文字,那麼它就有可能複用別的 Cell 的這些屬性了。此外,還有個問題,對偶數行 Cell 的 textLabel 的文字顏色的設置放在了初始一個 Cell 的 if 代碼塊裏,這樣在複用的時候,邏輯走不到這裏去,那麼也會出現複用問題。因此,上面的代碼須要改爲這樣:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"UITableViewCell"; UITableViewCell *cell = nil; cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; } cell.textLabel.text = @"Title"; if (indexPath.row % 2 == 0) { [cell.textLabel setTextColor:[UIColor redColor]]; cell.detailTextLabel.text = @"Detail"; } else { [cell.textLabel setTextColor:[UIColor blackColor]]; cell.detailTextLabel.text = nil; } return cell; }
總之在複用的時候須要記住:
設置 Cell 的存在差別性的那些屬性(包括樣式和內容)時,有了 if 最好就要有 else,要顯式的覆蓋全部可能性。
設置 Cell 的存在差別性的那些屬性時,代碼要放在初始化代碼塊的外部。
上面的代碼中,咱們展現了 - [UITableView dequeueReusableCellWithIdentifier:];
的用法。下面看看另幾個 API 的用法:
@property (weak, nonatomic) IBOutlet UITableView *myTableView; - (void)viewDidLoad { [super viewDidLoad]; // Setup table view. self.myTableView.delegate = self; self.myTableView.dataSource = self; [self.myTableView registerClass:[MyTableViewCell class] forCellReuseIdentifier:@"MyTableViewCell"]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"MyTableViewCell"; UITableViewCell *cell = nil; cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; cell.textLabel.text = @"Title"; if (indexPath.row % 2 == 0) { [cell.textLabel setTextColor:[UIColor redColor]]; } else { [cell.textLabel setTextColor:[UIColor blackColor]]; } return cell; }
能夠看到,- [UITableView dequeueReusableCellWithIdentifier:forIndexPath:];
必須搭配- [UITableView registerClass:forCellReuseIdentifier:];或者- [UITableView registerNib:forCellReuseIdentifier:];
使用。當有可重用的 Cell 時,前者直接拿來複用,並調用 - [UITableViewCell prepareForReuse]
方法;當沒有時,前者會調用 Identifier 對應的那個註冊的 UITableViewCell 類的 - [UITableViewCell initWithStyle:reuseIdentifier:]方法來初始化一個,這裏省去了你本身初始化的步驟。當你自定義了一個 UITableViewCell 的子類時,你能夠這樣來用。
UITableView 是咱們最經常使用來展現數據的控件之一,而且一般須要 UITableView 在承載較多內容的同時保證交互的流暢性,對 UITableView 的性能優化是咱們開發應用程序必備的技巧之一。
在前文「使用複用機制」一節,已經提到了 UITableView 的複用機制。如今就來看看 UITableView 在複用時最主要的兩個回調方法:- [UITableView tableView:cellForRowAtIndexPath:]和- [UITableView tableView:heightForRowAtIndexPath:]。UITableView 是繼承自 UIScrollView,因此在渲染的過程當中它會先肯定它的 contentSize 及每一個 Cell 的位置,而後纔會把複用的 Cell 放置到對應的位置。好比如今一共有 50 個 Cell,當前屏幕上顯示 5 個。那麼在第一次建立或 reloadData 的時候, UITableView 會先調用 50 次- [UITableView tableView:heightForRowAtIndexPath:]
肯定 contentSize 及每一個 Cell 的位置,而後再調用 5 次 - [UITableView tableView:cellForRowAtIndexPath:]
來渲染當前屏幕的 Cell。在滑動屏幕的時候,每當一個 Cell 進入屏幕時,都須要調用一次 - [UITableView tableView:cellForRowAtIndexPath:]和- [UITableView tableView:heightForRowAtIndexPath:]方法。
瞭解了 UITableView 的複用機制以及相關回調方法的調用次序,這裏就對 UITableView 的性能優化方案作一個總結:
UIView 有一個 opaque屬性,在你不須要透明效果時,你應該儘可能設置它爲 YES 能夠提升繪圖過程的效率。
在一個靜態的視圖裏,這點可能影響不大,可是當在一個能夠滾動的 Scroll View 中或是一個複雜的動畫中,透明的效果可能會對程序的性能有較大的影響。
若是你壓根不用 XIB,那就不須要看了。
在你須要重用某些自定義 View 或者由於歷史兼容緣由用到 XIB 的時候,你須要注意:當你加載一個 XIB 時,它的全部內容都會被加載,若是這個 XIB 裏面有個 View 你不會立刻就用到,你其實就是在浪費寶貴的內存。而加載 StoryBoard 時並不會把全部的 ViewController 都加載,只會按需加載。
基本上 UIKit 會把它全部的工做都放在主線程執行,好比:繪製界面、管理手勢、響應輸入等等。當你把全部代碼邏輯都放在主線程時,有可能由於耗時太長卡住主線程形成程序沒法響應、流暢性太差等問題。形成這種問題的大多數場景是由於你的程序把 I/O 操做放在了主線程,好比從硬盤或者網絡讀寫數據等等。
你能夠經過異步的方式來進行這些操做,把他們放在別的線程中處理。好比處理網絡請求時,你可使用 NSURLConnection 的異步調用 API:
+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler;
或者使用第三方的類庫,好比 AFNetworking。
當你作一些耗時比較長的操做時,你可使用 GCD、NSOperation、NSOperationQueue。好比 GCD 的常見使用方式:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // switch to another thread and perform your expensive operation dispatch_async(dispatch_get_main_queue(), ^{ // switch back to the main thread to update your UI }); });
關於 GCD 更多的知識,你能夠看看這篇文章:GCD。
當你從 App bundle 中加載圖片到 UIImageView 中顯示時,最好確保圖片的尺寸可以和 UIImageView 的尺寸相匹配(固然,須要考慮 @2x @3x 的狀況),不然會使得 UIImageView 在顯示圖片時須要作拉伸,這樣會影響性能,尤爲是在一個 UIScrollView 的容器裏。
有時候,你的圖片是從網絡加載的,這時候你並不能控制圖片的尺寸,不過你能夠在圖片下載下來後去手動 scale 一下它,固然,最好是在一個後臺線程作這件事,而後在 UIImageView 中使用 resize 後的圖片。
關於這一點SDWebImage進行了優化,若是你使用SDWebImage,它會幫你進行Scale優化。
咱們常常須要用到容器來轉載多個對象,咱們一般用到的包括:NSArray、NSDictionary、NSSet,它們的特性以下:
根據以上特性,在編程中須要選擇適合的容器。更多內容請看:Collections Programming Topics
如今愈來愈多的應用須要跟服務器進行數據交互,當交互的數據量較大時,網絡傳輸的時延就會較長,經過啓動數據壓縮功能,尤爲是對於文本信息,能夠下降網絡傳輸的數據量,從而減短網絡交互的時間。
一個好消息是當你使用 NSURLConnection 或者基於此的一些網絡交互類庫(好比 AFNetworking)時 iOS 已經默認支持 GZIP 壓縮。而且,不少服務器已經支持發送壓縮數據。
經過在服務器和客戶端程序中啓用對網絡交互數據的壓縮,是一條提升應用程序性能的途徑。
當你的程序中須要展現不少的 View 的時候,這就意味着須要更多的 CPU 處理時間和內存空間,這個狀況對程序性能的影響在你使用 UIScrollView 來裝載和呈現界面時會變得尤其顯著。
處理這種狀況的一種方案就是向 UITableView 和 UICollectionView 學習,不要一次性把全部的 subviews 都建立出來,而是在你須要他們的時候建立,而且用複用機制去複用他們。這樣減小了內存分配的開銷,節省了內存空間。
「懶加載機制」就是把建立對象的時機延後到不得不須要它們的時候。這個機制經常用在對一個類的屬性的初始化上,好比:
- (UITableView *)myTableView { if (!_myTableView) { CGRect viewBounds = self.view.bounds; _myTableView = [[UITableView alloc] initWithFrame:viewBounds style:UITableViewStylePlain]; _myTableView.showsHorizontalScrollIndicator = NO; _myTableView.showsVerticalScrollIndicator = NO; _myTableView.backgroundColor = [UIColor whiteColor]; [_myTableView setSeparatorStyle:UITableViewCellSeparatorStyleNone]; _myTableView.dataSource = self; _myTableView.delegate = self; } return _myTableView; }
只有當咱們第一次用到 self.myTableView 的時候採起初始化和建立它。
可是,存在這樣一種場景:你點擊一個按鈕的時候,你須要顯示一個 View,這時候你有兩種實現方案:
因此當你考慮使用何種方案時,你須要根據現實的狀況來參考,去權衡到底哪一個因素纔是影響性能的瓶頸,而後再作出選擇。
在開發咱們的程序時,一個很重要的經驗法則就是:對那些更新頻度低,訪問頻度高的內容作緩存。
有哪些東西使咱們能夠緩存的呢?好比下面這些:
+ (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"]; ]return request; }
關於 HTTP 緩存的更多內容能夠關注 NSURLCache。關於緩存其餘非 HTTP 請求的內容,能夠關注 NSCache。對於圖片緩存,能夠關注一個第三方庫 SDWebImage。
當咱們爲一個 UIButton 設置背景圖片時,對於這個背景圖片的處理,咱們有不少種方案,你可使用全尺寸圖片直接設置,還能夠用 resizable images,或者使用 CALayer、CoreGraphics 甚至 OpenGL 來繪製。
固然,不一樣的方案的編碼複雜度不同,性能也不同。關於圖形繪製的不一樣方案的性能問題,能夠看看:Designing for iOS: Graphics Performance
簡而言之,使用 pre-rendered 的圖片會更快,由於這樣就不須要在程序中去建立一個圖像,並在上面繪製各類形狀了(Offscreen Rendering,離屏渲染)。可是缺點是你必須把這些圖片資源打包到代碼包,從而須要增長程序包的體積。這就是爲何 resizable images 是一個很棒的選擇:不須要全尺寸圖,讓 iOS 爲你繪製圖片中那些能夠拉伸的部分,從而減少了圖片體積;而且你不須要爲不一樣大小的控件準備不一樣尺寸的圖片。好比兩個按鈕的大小不同,可是他們的背景圖樣式是同樣的,你只須要準備一個對應樣式的 resizable image,而後在設置這兩個按鈕的背景圖的時候分別作拉伸就能夠了。
可是一味的使用使用預置的圖片也會有一些缺點,好比你作一些簡單的動畫的時候各個幀都用圖片疊加,這樣就可能要使用大量圖片。
總之,你須要去在圖形繪製的性能和應用程序包的大小上作權衡,找到最合適的性能優化方案。
關於內存警告,蘋果的官方文檔是這樣說的:
If your app receives this warning, it must free up as much memory as possible. The best way to do this is to remove strong references to caches, image objects, and other data objects that can be recreated later.
咱們能夠經過這些方式來得到內存警告:
在 AppDelegate 中實現 - [AppDelegate applicationDidReceiveMemoryWarning:]代理方法。
在 UIViewController 中重載 didReceiveMemoryWarning方法。
監聽 UIApplicationDidReceiveMemoryWarningNotification通知。
當經過這些方式監聽到內存警告時,你須要立刻釋放掉不須要的內存從而避免程序被系統殺掉。
好比,在一個 UIViewController 中,你能夠清除那些當前不顯示的 View,同時能夠清除這些 View 對應的內存中的數據,而有圖片緩存機制的話也能夠在這時候釋放掉不顯示在屏幕上的圖片資源。
可是須要注意的是,你這時清除的數據,必須是能夠在從新獲取到的,不然可能由於必要數據爲空,形成程序出錯。在開發的時候,可使用 iOS Simulator 的 Simulate memory warning的功能來測試你處理內存警告的代碼。
在 Objective-C 中有些對象的初始化過程很緩慢,好比:NSDateFormatter和 NSCalendar,可是有些時候,你也不得不使用它們。爲了這樣的高開銷的對象成爲影響程序性能的重要因素,咱們能夠複用它們。
好比,咱們在一個類裏添加一個 NSDateFormatter 的對象,並使用懶加載機制來使用它,整個類只用到一個這樣的對象,並只初始化一次:
保證應用快速啓動的指導原則:
// in your .h or inside a class extension @property (nonatomic, strong) NSDateFormatter *dateFormatter; // inside the implementation (.m) // When you need, just use self.dateFormatter - (NSDateFormatter *)dateFormatter { if (! _dateFormatter) { _dateFormatter = [[NSDateFormatter alloc] init]; [_dateFormatter setDateFormat:@"yyyy-MM-dd a HH:mm:ss EEEE"]; } return _dateFormatter; }
可是上面的代碼在多線程環境下會有問題,因此咱們能夠改進以下:
// no property is required anymore. The following code goes inside the implementation (.m) - (NSDateFormatter *)dateFormatter { static NSDateFormatter *dateFormatter; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _dateFormatter = [[NSDateFormatter alloc] init]; [_dateFormatter setDateFormat:@"yyyy-MM-dd a HH:mm:ss EEEE"]; }); return dateFormatter; }
這樣就線程安全了。(關於多線程 GCD 的知識,能夠看看這篇文章:GCD)
須要注意的是:設置 NSDateFormatter 的 date format 跟建立一個新的 NSDateFormatter 對象同樣慢,所以當你的程序中要用到多種格式的 date format,而每種又會用到屢次的時候,你能夠嘗試爲每種 date format 建立一個可複用的 NSDateFormatter 對象來提供程序的性能。
快速啓動應用對於用戶來講能夠留下很好的印象。尤爲是第一次使用時。儘可能將啓動過程當中的處理分拆成各個異步處理流,好比:網絡請求、數據庫訪問、數據解析等等。
避免臃腫的 XIB 文件,由於它們會在你的主線程中進行加載。重申:Storyboard 沒這個問題,放心使用。
注意:在測試程序啓動性能的時候,最好用與 Xcode 斷開鏈接的設備進行測試。由於 watchdog 在使用 Xcode 進行調試的時候是不會啓動的。
咱們的 iOS 應用程序與服務器進行交互時,一般採用的數據格式就是 JSON 和 XML 兩種。那麼在選擇哪種時,須要考慮到它們的優缺點。
JSON 文件的優勢是:
缺點是:
而 XML 文件的優缺點則恰好反過來。 XML 的一個優勢就是它可使用 SAX 來解析數據,從而能夠邊加載邊解析,不用等全部數據都讀取完成了才解析。這樣在處理很大的數據集的時提升性能和下降內存消耗。
因此,你須要根據具體的應用場景來權衡使用何種數據格式。
咱們一般有兩種方式來設置一個 View 的背景圖片:
// You could also achieve the same result in Interface Builder UIImageView *backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background"]]; [self.view addSubview:backgroundView];
可是,當你計劃採用一個小塊的模板樣式圖片,就像貼瓷磚那樣來重複填充整個背景時,你應該用- [UIColor colorWithPatternImage:]self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"backgroundPattern"]];
UIWebView 在咱們的應用程序中很是有用,它能夠便捷的展現 Web 的內容,甚至作到你用標準的 UIKit 控件較難作到的視覺效果。可是,你應該注意到你在應用程序裏使用的 UIWebView 組件不會比蘋果的 Safari 更快。這是首先於 Webkit 的 Nitro Engine 引擎。因此,爲了獲得更好的性能,你須要優化你的網頁內容。
優化第一步就是避免過量使用 Javascript,例如避免使用較大的 Javascript 框架,好比 jQuery。通常使用原生的 Javascript 而不是依賴於 Javascript 框架能夠得到更好的性能。
優化第二步,若是可能的話,能夠異步加載那些不影響頁面行爲的 Javascript 腳本,好比一些數據統計腳本。
優化第三步,老是關注你在頁面中所使用的圖片,根據具體的場景來顯示正確尺寸的圖片,同時也可使用上面提到的「使用 Sprites Sheets」的方案來在某些地方減小內存消耗和提升速度。
什麼是「離屏渲染」?離屏渲染,即 Off-Screen Rendering。與之相對的是 On-Screen Rendering,即在當前屏幕渲染,意思是渲染操做是用於在當前屏幕顯示的緩衝區進行。那麼離屏渲染則是指圖層在被顯示以前是在當前屏幕緩衝區之外開闢的一個緩衝區進行渲染操做。
離屏渲染須要屢次切換上下文環境:先是從當前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結束之後,將離屏緩衝區的渲染結果顯示到屏幕上又須要將上下文環境從離屏切換到當前屏幕,而上下文環境的切換是一項高開銷的動做。
一般圖層的如下屬性將會觸發離屏渲染:
UIImageView *imageView = [[UIImageView alloc] initWithFrame:...];
// Setup the shadow ... imageView.layer.shadowOffset = CGSizeMake(5.0f, 5.0f); imageView.layer.shadowRadius = 5.0f; imageView.layer.shadowOpacity = 0.6;
可是上面這樣的作法有一個壞處是:將觸發 Core Animation 作離屏渲染形成開銷。UIImageView *imageView = [[UIImageView alloc] initFrame:...]; // Setup the shadow ... imageView.layer.shadowPath = [[UIBezierPath bezierPathWithRect: CGRectMake(imageView.bounds.origin.x+5, imageView.bounds.origin.y+5,imageView.bounds.size.width, imageView.bounds.size.height)] CGPath]; imageView.layer.shadowOpacity = 0.6;
若是圖層是一個簡單幾何圖形如矩形或者圓角矩形(假設不包含任何透明部分或者子圖層),經過設置 ShadowPath 屬性來建立出一個對應形狀的陰影路徑就比較容易,並且 Core Animation 繪製這個陰影也至關簡單,不會觸發離屏渲染,這對性能來講頗有幫助。若是你的圖層是一個更復雜的圖形,生成正確的陰影路徑可能就比較難了,這樣子的話你能夠考慮用繪圖軟件預先生成一個陰影背景圖。CALayer 有一個屬性是 shouldRasterize
經過設置這個屬性爲 YES 能夠將圖層繪製到一個屏幕外的圖像,而後這個圖像將會被緩存起來並繪製到實際圖層的 contents 和子圖層,若是很不少的子圖層或者有複雜的效果應用,這樣作就會比重繪全部事務的全部幀來更加高效。可是光柵化原始圖像須要時間,並且會消耗額外的內存。這是須要根據實際場景權衡的地方。
當咱們使用得當時,光柵化能夠提供很大的性能優點,可是必定要避免在內容不斷變更的圖層上使用,不然它緩存方面的好處就會消失,並且會讓性能變的更糟。
爲了檢測你是否正確地使用了光柵化方式,能夠用 Instrument 的 Core Animation Template 查看一下Color Hits Green and Misses Red
項目,看看是否已光柵化圖像被頻繁地刷新(這樣就說明圖層並非光柵化的好選擇,或則你無心間觸發了沒必要要的改變致使了重繪行爲)。
若是你最後設置了 shouldRasterize 爲 YES,那也要記住設置 rasterizationScale 爲合適的值。在咱們使用 UITableView 和 UICollectionView 時常常會遇到各個 Cell 的樣式是同樣的,這時候咱們可使用這個屬性提升性能:
cell.layer.shouldRasterize = YES; cell.layer.rasterizationScale = [[UIScreen mainScreen] scale];
可是,若是你的 Cell 是樣式不同,好比高度不定,排版多變,那就要慎重了。
在 iOS 中能夠用來進行數據持有化的方案包括:
NSAutoreleasePool 是用來管理一個自動釋放內存池的機制。在咱們的應用程序中一般都是 UIKit 隱式的自動使用 Autorelease Pool,可是有時候咱們也能夠顯式的來用它。
好比當你須要在代碼中建立許多臨時對象時,你會發現內存消耗激增直到這些對象被釋放,一個問題是這些內存只會到 UIKit 銷燬了它對應的 Autorelease Pool 後纔會被釋放,這就意味着這些內存沒必要要地會空佔一些時間。這時候就是咱們顯式的使用 Autorelease Pool 的時候了,一個示例以下:
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. */ } }
上面的代碼在每一輪迭代中都會釋放掉臨時對象,從而緩解內存壓力,提升性能。
在 iOS 應用中加載圖片一般有 - [UIImage imageNamed:]
和 -[UIImage imageWithContentsOfFile:]
兩種方式。它們的不一樣在於前者會對圖片進行緩存,然後者只是簡單的從文件加載文件。
UIImage *img = [UIImage imageNamed:@"myImage"]; // caching // or UIImage *img = [UIImage imageWithContentsOfFile:@"myImage"]; // no caching
在整個程序運行的過程當中,當你須要加載一張較大的圖片,而且只會使用它一次,那麼你就不必緩存這個圖片,這時你可使用 -[UIImage imageWithContentsOfFile:]
,這樣系統也不會浪費內存來作緩存了。固然,若是你會屢次使用到一張圖時,用 - [UIImage imageNamed:]
就會高效不少,由於這樣就不用每次都從硬盤上加載圖片了。
在前文中,咱們已經講到了經過複用或者單例來提升 NSDateFormatter 這個高開銷對象的使用效率。可是若是你要追求更快的速度,你能夠直接使用 C 語言替代 NSDateFormatter 來解析 date,你能夠看看這篇文章:link,其中展現瞭解析 ISO-8601 date string 的代碼,你能夠根據你的需求改寫。完成的代碼見:SSToolkit/NSDate+SSToolkitAdditions.m。
固然,若是你可以控制你接受到的 date 的參數的格式,你必定要儘可能選擇 Unix timestamps
格式,這樣你可使用:
- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp { return [NSDate dateWithTimeIntervalSince1970:timestamp]; }
這樣你能夠輕鬆的將時間戳轉化爲 NSDate 對象,而且效率甚至高於上面提到的 C 函數。
須要注意的是,不少 web API 返回的時間戳是以毫秒爲單位的,由於這更利於 Javascript 去處理,可是上面代碼用到的方法中 NSTimeInterval 的單位是秒,因此當你傳參的時候,記得先除以 1000。