iOS block並發

來源:互聯網
上載者:User

標籤:

 iOS block並發2012-06-13 09:31 1351人閱讀 評論(0) 收藏 舉報iosuiviewnetwork任務threadimage

這篇文章轉自 http://anxonli.iteye.com/blog/1097777,集中與iOS的多核編程和記憶體管理,大家完全可以使用蘋果的多核編程架構來寫出更加responsive的應用。

多核運算

在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。

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

dispatch_queue_t network_queue;

network_queue = dispatch_queue_create(“com.myapp.network”, nill);

例如,我們圖片讀取放到network_queue來進行非同步執行

dispatch_async(network_queue, ^{

UIImage *cellImage = [self loadMyImageFromNetwork:image_url];

// 將圖片cache到本地

[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官方文檔請參考:

iOS block並發

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.