iOS block併發

 

iOS block併發

這篇文章轉自 http://anxonli.iteye.com/blog/1097777,集中與iOS的多核編程和內存管理,你們徹底可使用蘋果的多核編程框架來寫出更加responsive的應用。ios

多核運算程序員

在iOS中concurrency編程的框架就是GCD(Grand Central Dispatch), GCD的使用很是簡單。它把任務分派到不一樣的queue隊列來處理。開發者把任務代碼裝到一個個block裏面,操做系統把這些任務代碼分派到不一樣的資源 裏去處理,一個簡單的例子來講,爲何初學者寫tableview的時候,滑動列表時總會很卡,由於不少初學者把圖片裝載放到main thread主線程去執行,例如咱們要滑動暢順的話,iOS最快能夠1秒內刷新60次,如何你的一個cell的文字和圖片裝載超過1/60秒的話,天然就 會卡。因此通常咱們會把圖片裝載這些須要多點時間的移出main thread來處理,對於用GCD來講,就是把圖片載入放到另一個queue隊列中來異步執行,當資源準備好了後,放回到main thread中顯示出來。main thread在GCD中就是main queue。objective-c

建立一個新queue隊列的代碼:編程

dispatch_queue_t network_queue;網絡

network_queue = dispatch_queue_create(「com.myapp.network」, nill);多線程

例如,咱們圖片讀取放到network_queue來進行異步執行併發

dispatch_async(network_queue, ^{app

UIImage *cellImage = [self loadMyImageFromNetwork:image_url];框架

// 將圖片cache到本地iview

[self cacheImage:cellImage];

…..

} );

dispatch_async的意思就是將任務進行異步並行處理,不必定須要一個任務處理完後才能處理下一個。以上代碼 loadMyImageFromNetwork的意思就是從網絡中讀取圖片,這個任務交給network_queue來處理。這樣讀取圖片的時間過長也不 會阻塞主線程界面的處理。

當咱們處理完圖片後,應該更新界面,從queue的概念去設計,就是要將更新界面的代碼放到main queue中去,由於iOS裏面永遠是主線程來刷新重畫UI。因此代碼應該爲,

dispatch_async(network_queue, ^{

UIImage *cellImage = [self loadMyImageFromNetwork:image_url];

// 將圖片cache到本地

[self cacheImage:cellImage];

// 回到主線程

dispatch_async(dispatch_get_main_queue(), ^{

// 顯示圖片到界面

[self displayImageToTableView:cellImage];

}];

} );

dispatch_get_main_queue() 函數就是返回主線程,^{} 封裝的就是任務代碼,這樣嵌套方式就能夠從一個隊列queue,跳到另外一個queue,就是這麼簡單。

咱們通常能夠把networking有關的代碼放到一個queue,把圖片resize的代碼放到另一個queue,處理完後更新界面,只須要嵌套跳回 到 main queue。這樣加上幾行代碼,你的程序就能夠利用到系統多核資源,把具體的調度工做交給了操做系統本身來分配。有了這樣的代碼,無論你的硬件是單核,雙 核仍是iMac的4核,甚至8核,均可以很是好地並行處理。

內存管理

我一直驚歎iOS和Objective-C內存處理能力,例如iPad版本的iWork,Pages應用就是一個內存處理技術應用的鬼斧神工之做。想一想 256M內存的iPad,能夠帶來如此的華麗的界面同時得到如此流暢的用戶體驗,真是不簡單。緣由就是iOS一直提倡開發者在有限硬件資源內寫出最優化的 代碼,使用CPU最少,佔用內存最小。(如下代碼適用於iOS SDK 4.1, 因爲新SDK 4.2對內存使用有新改動,因此可能有不一樣。。。)

儘可能少的UIView層

一般咱們喜歡把不少控件層(UILabel,UIButton,UIView等)一塊兒放到一個大的UIView容器來顯示咱們的內容,這個方法通常是能夠 的,可是若是要常常從新刷新內容的大區域界面,多數發生在iPad的應用中,這個方法會帶來過多的內存使用和動畫的延遲(比較卡),例 如,scrollview的動畫比較卡,又或者,常常收到內存警告。其中一個重要緣由是每一個控件,特別是透明底的,會屢次從新繪製(drawRect)過 多。其解決辦法是,儘可能將幾個控件合併到一個層上來顯示,這樣系統會減小系統調用drawRect,從而帶來性能上的提高。

很簡單的一個例子,就是iNotes提供手寫功能,用戶能夠在iPad屏幕上寫出不一樣的筆畫,開始的設計是,用戶每寫一劃,iNotes就會生成一個新的 透明底UIView來保持這個筆畫,用戶寫了10筆,系統就生產了10個UIView,每一個view的大小都是整個屏幕的,以便用戶的undo操做。這個 方案帶來嚴重的內存問題,由於系統將每一個層都保持一個bitmap圖,一個像素須要4bit來算,一個層的大小就是 4x1024x768 ~ 3M, 10個層就是 10x3M = 30M,很明顯,iPad很快爆出內存警告。

這個例子最後的方案是,全部筆畫都畫在同一個層,iNotes能夠保存筆畫的點進行undo操做。這樣的方案就是不管用戶畫多少筆畫,界面重畫須要的資源都是同樣的。

顯示最佳尺寸的圖片

不少程序員比較懶,網絡上拿下來的圖片,直接就用UIImageView將其顯示給用戶,這樣的後果就是,程序須要一直保存着大尺寸的圖片到內存。解決辦法應該是先將圖片縮小到須要顯示的尺寸,釋放大尺寸圖片的內存,而後再顯示到界面給用戶。

儘可能使用圖片pattern,而不是一張大的圖片

例如,不少界面設計者喜歡在界面上放一個大底圖,但這個底圖是總是佔用着內存的,最佳方案是,設計出一個小的pattern圖,而後用這個方案顯示成底圖。

UIImage *smallImage = [[UIImage alloc] initWithContentsOfFile:path];

backgroundView.backgroundColor = [UIColor colorWithPatternImage:smallImage];

[smallImage release];

使用完資源後,當即釋放

通常objective-c的習慣是,用完的資源要當即釋放,由於明白何時用完某個資源的是程序員你本身。例如,咱們要讀較大的圖片,把它縮小後,顯示到界面去。當大圖片使用完成後,應該當即釋放。代碼以下:

UIImage *fullscreenImage = [[UIImage alloc] initWithContentOfFile:path];

UIImage *smallImage = [self resizeImage:fullscreenImage];

[fullscreenImage release];

imageView.image = smallImage;……

循環中大量生成的自動釋放autorelease對象,能夠考慮使用autorelease pool封裝。代碼範例:

for(UIView *subview in bigView.subviews) {

// 使用autorelease pool自動釋放對象池

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];UIImageView *imageView = (UIImageView *)subview;

// subview處理代碼

…….

// 銷燬自動釋放對象

[pool  drain];

}

自動釋放對象池把每一個循環內生成的臨時對象使用完後當即釋放

以上的意見是本人多年來編寫iPad/iPhone程序的經驗,另外iOS4.0的multi-tasking特性發布後,程序能夠被調入後臺運行,蘋果 工程師的意見是,進入後臺運行時,你的應用應該釋放掉能釋放的對象,儘可能保持在16M左右,這樣別的程序運行時纔不容易把你的應用擠掉。

 

 

 

 

 

 

 

、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

 

 

 

 

 

 

iphone開發過程當中,若是遇到加載大數據或者涉及到網絡通訊狀況時,就須要在後臺線程來完成這些事情。

除了NSThread以外,iphone還提供了一套GCD機制幫助開發者來實現多線程開發。

同NSThread相比,GCD的運行效率更高,開發更簡單。

GCD的基礎是dispatch queue和block。

 

1. block能夠簡單理解爲一個任務。block在程序中的表現形式相似: 

1 NSString * URL = @"......";

2

3 ^{

4

5     UIImage * image = [UIImage imageWithURL:URL];

6

7 };

從上例能夠看出,block能夠引用外部做用域的數據。這也是很block和普通函數的區別,block會保存當前執行的上下文。

 

2. dispatch queue是一個FIFO任務隊列,能夠將一些block壓入這個隊列中,系統會按照順序來執行這些block。

dispatch_async()

系統中默認提供了三種dispatch queue:

a. Main. 若是某個block但願在主線程完成,能夠將其push到main dispatch queue中。

b. Concurrent. 系統會自動建立三個不一樣優先級的dispatch queue。不能保證block嚴格按照順序執行。

c. Serial. 須要用戶手動建立,可以保證block嚴格按照push的順序執行。

 

下面是一段異步加載網絡圖像的例子:

 1 UIImageView * imageView = [[UIImageView alloc] init];

 2

 3   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

 4

 5     UIImage * image = ;//網絡拉取代碼

 6

 7     dispatch_async(dispatch_get_main_queue (), ^{

 8

 9       imageView.image = image; // 在主線程中更新imageview

10

11     });

12

13   });

從以上代碼能夠看出,GCD的先後臺線程同步通知機制要比NSThread優雅和方便不少。

 

apple官方文檔請參考:

相關文章
相關標籤/搜索