1。GCD之dispatch queue
http://www.cnblogs.com/scorpiozj/archive/2011/07/25/2116459.html
2。iOS中GCD的魔力
http://blog.csdn.net/favormm/article/details/6453260
3。官方 ,內容真的很多
http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1
4.詳解IOS開發應用之並發Dispatch Queues
http://mobile.51cto.com/iphone-283323.htm
5。斯坦福大學關於gcd的講義
http://www.stanford.edu/class/cs193p/cgi-bin/drupal/system/files/lectures/Lecture%2013_0.pdf
gcd其實就是牛逼簡化版的多線程。gcd和block是親兄弟,所以學習gcd前需要瞭解block,不知道也沒事,看看代碼就明白了。
函數原型
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
async表明非同步運行,.(除了async,還有sync,delay,本文以async為例).
queue則是你把任務交給誰來處理了
block代表的是你要做的事情
queue有三種
Main: tasks execute serially on your application’s main thread
Concurrent: tasks start executing in FIFO order, but can run concurrently.
Serial: tasks execute one at a time in FIFO order
(1)serial queues(串列隊列)又稱私人調度隊列(private),一般用再對特定資源的同步訪問上。我們可以根據需要建立任意數量的串列隊列,每一個串列隊列之間是並發的。
(2)並行隊列,又稱global dispatch queue。並行隊列雖然可以並發的執行多個任務,但是任務開始執行的順序和其排入佇列的順序相同。我們自己不能去建立並行調度隊列。只有三個可用的global concurrent queues。
(3)main dispatch queue 是一個全域可用的串列隊列,其在行用程式的主線程上執行任務。此隊列的任務和應用程式的主迴圈(run loop)要執行的事件來源交替執行。因為其運行在應用程式的主線程,main queue經常用來作為應用程式的一個同步點
先看一段代碼
@interface UIImageView (DispatchLoad) - (void) setImageFromUrl:(NSString*)urlString; - (void) setImageFromUrl:(NSString*)urlString completion:(void (^)(void))completion; @end
#import "UIImageView+DispatchLoad.h" @implementation UIImageView (DispatchLoad) - (void) setImageFromUrl:(NSString*)urlString { [self setImageFromUrl:urlString completion:NULL]; } - (void) setImageFromUrl:(NSString*)urlString completion:(void (^)(void))completion { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"Starting: %@", urlString); UIImage *avatarImage = nil; NSURL *url = [NSURL URLWithString:urlString]; NSData *responseData = [NSData dataWithContentsOfURL:url]; avatarImage = [UIImage imageWithData:responseData]; NSLog(@"Finishing: %@", urlString); if (avatarImage) { dispatch_async(dispatch_get_main_queue(), ^{ self.image = avatarImage; }); dispatch_async(dispatch_get_main_queue(), completion); } else { NSLog(@"-- impossible download: %@", urlString); } }); } @end
以上代碼主要是實現,映像非同步載入。分解一下:1>添加到gcd隊列dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
這個代碼主要實現,將映像載入block添加到queue隊列中,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
這個是擷取全域並行隊列(global dispatch queue),系統隊列,只有3個。2>載入映像。這個濾過3>通知或更新主線程
dispatch_async(dispatch_get_main_queue(), ^{ self.image = avatarImage; }); dispatch_async(dispatch_get_main_queue(), completion);
“block的一個優勢是可以使用其自己範圍外的變數,例如,一個block可以讀取其父範圍的變數值,此值是copy到了block heap的資料結構中。當block被加入到dispatch queue中,這些值通常為唯讀形式。”
而更新UI只能在主線程中實現,所以調用主線程函數 completion
這樣就完成了非同步載入映像的流程。
當想要任務按照某一個特定的順序執行時,串列隊列是很有用的。串列隊列在同一個時間只執行一個任務。我們可以使用串列隊列代替鎖去保護共用的資料。和鎖不同,一個串列隊列可以保證任務在一個可預知的順序下執行。和並發隊列不同,我們要自己去建立和管理串列隊列,可以建立任意數量的串列隊列。當我們建立串列隊列時,應出於某種目的,如保護資源,或者同步應用程式的某些關鍵行為。
下面的代碼錶述了怎麼建立一個自訂的串列隊列,函數dispath_queue_create需要兩個參數,隊列的名字,隊列的屬性。調試器和效能工具顯示隊列的名字協助我們去跟蹤任務是如何執行,隊列的屬性被保留供將來使用,應該為NULL
- dispatch_queue_t queue;
- queue = dispatch_queue_create("com.example.MyQueue", NULL);
除了自己建立的自訂隊列,系統會自動的給我建立一個串列隊列並和應用程式的主線程綁定到一起。下面講述如何獲得它。
貼幾段斯坦福大學關於gcd的代碼,這段代碼逐步示範了如何修正錯誤,其中用到的既是串列隊列
1。這個是原始代碼
- (void)viewWillAppear:(BOOL)animated{ NSData *imageData = [FlickrFetcher imageDataForPhotoWithURLString:photo.URL]; UIImage *image = [UIImage imageWithData:imageData]; self.imageView.image = image; self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height); self.scrollView.contentSize = image.size;}
2。這個是採用gcdd的代碼,裡面有錯誤3處
- (void)viewWillAppear:(BOOL)animated{ dispatch_queue_t downloadQueue = dispatch_queue_create(“Flickr downloader”, NULL); dispatch_async(downloadQueue, ^{ NSData *imageData = [FlickrFetcher imageDataForPhotoWithURLString:photo.URL]; UIImage *image = [UIImage imageWithData:imageData]; self.imageView.image = image; self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height); self.scrollView.contentSize = image.size; });}
3。第一個錯誤,UI更新只能在主線程中 Problem! UIKit calls can only happen in the main thread!改正後如下:
- (void)viewWillAppear:(BOOL)animated{ dispatch_queue_t downloadQueue = dispatch_queue_create(“Flickr downloader”, NULL); dispatch_async(downloadQueue, ^{ NSData *imageData = [FlickrFetcher imageDataForPhotoWithURLString:photo.URL]; dispatch_async(dispatch_get_main_queue(), ^{ UIImage *image = [UIImage imageWithData:imageData]; self.imageView.image = image; self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height); self.scrollView.contentSize = image.size; });}); }
4。第二個錯誤,NSManagedObjectContext並不是安全執行緒的,gcd中訪問成員變數有危險Problem! NSManagedObjectContext is not thread safe,
so we can’t call photo.URL in downloadQueue’s t
改正後如下:
- (void)viewWillAppear:(BOOL)animated{ NSString *url = photo.URL; dispatch_queue_t downloadQueue = dispatch_queue_create(“Flickr downloader”, NULL); dispatch_async(downloadQueue, ^{ NSData *imageData = [FlickrFetcher imageDataForPhotoWithURLString:url]; dispatch_async(dispatch_get_main_queue(), ^{ UIImage *image = [UIImage imageWithData:imageData]; self.imageView.image = image; self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height); self.scrollView.contentSize = image.size;}); });}
5。第三個錯誤,隊列建立後沒有釋放,記憶體泄露
- (void)viewWillAppear:(BOOL)animated{ NSString *url = photo.URL; dispatch_queue_t downloadQueue = dispatch_queue_create(“Flickr downloader”, NULL); dispatch_async(downloadQueue, ^{ NSData *imageData = [FlickrFetcher imageDataForPhotoWithURLString:url]; dispatch_async(dispatch_get_main_queue(), ^{ UIImage *image = [UIImage imageWithData:imageData]; self.imageView.image = image; self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height); self.scrollView.contentSize = image.size;}); });dispatch_release(downloadQueue); //won’tactuallygoawayuntilqueueisempty }