http://blog.sina.com.cn/s/blog_45e2b66c01010dhd.html
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 13_0.pdf
gcd其實就是牛逼簡化版的多線程。gcd和block是親兄弟,所以學習gcd前需要了解block,不知道也沒事,看看代碼就明白了。
GCD是和block緊密相連的,所以最好先了解下block(可以看我之前收藏的一篇文章).GCD是C level的函數,這意味着它也提供了C的函數指針作為參數,方便了C程序員.
下面首先來看GCD的使用:
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
async表明異步運行,block代表的是你要做的事情,queue則是你把任務交給誰來處理了.(除了async,還有sync,delay,本文以async為例).
之所以程序中會用到多線程是因為程序往往會需要讀取數據,然后更新UI.為了良好的用戶體驗,讀取數據的操作會傾向於在后台運行,這樣以避免阻塞主線程.GCD里就有三種queue來處理.
1. Main queue:
顧名思義,運行在主線程,由dispatch_get_main_queue獲得.和ui相關的就要使用Main Queue.
2.Serial quque(private dispatch queue,其中dispatch_queue_t就是一種)
每次運行一個任務,可以添加多個,執行次序FIFO. 通常是指程序員生成的,比如:
NSDate *da = [NSDate date]; NSString *daStr = [da description];
const char *queueName = [daStr UTF8String];
dispatch_queue_t myQueue = dispatch_queue_create(queueName, NULL);
3. Concurrent queue(global dispatch queue,其中dispatch_time_t就是一種):
可以同時運行多個任務,每個任務的啟動時間是按照加入queue的順序,結束的順序依賴各自的任務.使用dispatch_get_global_queue獲得.
所以我們可以大致了解使用GCD的框架:
dispatch_async(getDataQueue,^{ //獲取數據,獲得一組后,刷新UI. dispatch_aysnc (mainQueue,^{ //UI的更新需在主線程中進行 }; } )
在ios,blocks是對象,它封裝了一段代碼,這段代碼可以在任何時候執行。Blocks可以作為函數參數或者函數的返回值,而其本身又可以帶輸入參數或返回值。它和傳統的函數指針很類似,但是有區別:blocks是inline的,並且它對局部變量是只讀的。
Ios4已經直接支持blocks,
Blocks的定義:
int (^Multiply)(int, int) = ^(int num1, int num2) {return num1 * num2;};
定義了一個Multiply的blocks對象,它帶有兩個int參數,返回int。等式右邊就是blocks的具體實現,注意{}blocks體里的;。
Blocks可以訪問局部變量,但是不能修改。
int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
multiplier ++;//編譯報錯
return num * multiplier;
};
如果要修改就要加關鍵字:__block
__block int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
multiplier ++;//這樣就可以了
return num * multiplier;
};
作為函數的參數,blocks某種意義上替代了回調函數或者delegate。當函數調用了,假設某個事件觸發,這時blocks里的內容就會運行。這樣有利於代碼的整合和閱讀,你不需要到處去實現委托方法了。
系統API中已經有很多支持blocks參數了
· Completion handlers
· Notification handlers
· Error handlers
· Enumeration
· View animation and transitions
· Sorting
函數原型
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’t actually go away until queue is empty }
