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队列
这个代码主要实现,将图像加载block添加到queue队列中,dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
这个是获取全局并行队列(global dispatch queue),系统队列,只有3个。dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
2>加载图像。这个滤过3>通知或更新主线程
“block的一个优势是可以使用其自己作用域外的变量,例如,一个block可以读取其父作用域的变量值,此值是copy到了block heap的数据结构中。当block被加入到dispatch queue中,这些值通常为只读形式。”dispatch_async(dispatch_get_main_queue(), ^{ self.image = avatarImage; }); dispatch_async(dispatch_get_main_queue(), completion);
而更新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
改正后如下:
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; }); }); }
- (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 }