一、什么是GCD?
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。该方法在Mac OS X 10.6雪豹中首次推出,并随后被引入到了iOS4.0中。GCD是一个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术。
二、什么时候使用多线程
我个人理解,在实际开发中我们常常把那些比较耗时且与UI无关的操作放到非主线程中去执行,避免这些耗时操作阻塞主线程导致界面操作起来很卡。比如:加载网络数据、本地存储、读取、更新大量数据这些情况都应该使用多线程。
三、没有GCD之前
在GCD没有出现之前,在处理多线程的时候你可能用过(NSThread),(NSOperationQueue和NSInvocationOperation结合),或者是使用iOS提供的以下几个便捷方法:
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg NS_AVAILABLE(10_5, 2_0);
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
以上几种方法都有一个共同的缺点就是代码比较分散,降低程序的可读性
例如下面这段代码:(版权为唐巧)
static NSOperationQueue * queue; - (IBAction)someClick:(id)sender { self.indicator.hidden = NO; [self.indicator startAnimating]; queue = [[NSOperationQueue alloc] init]; NSInvocationOperation * op = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil] autorelease]; [queue addOperation:op]; } - (void)download { NSURL * url = [NSURL URLWithString:@"http://www.youdao.com"]; NSError * error; NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error]; if (data != nil) { [self performSelectorOnMainThread:@selector(download_completed:) withObject:data waitUntilDone:NO]; } else { NSLog(@"error when download:%@", error); [queue release]; } } - (void) download_completed:(NSString *) data { NSLog(@"call back"); [self.indicator stopAnimating]; self.indicator.hidden = YES; self.content.text = data; [queue release]; }
四、有了GCD以后
因为GCD采用了block的语法,因此我们可以把操作都放到block的代码块当中去执行(强烈建议在阅读本文之前先了解一些block的相关知识),还有一个优点就是GCD的API全部是c语言且是苹果强烈推荐使用,因此个人觉得在处理效率上应该相对其他几种方式要高一些。
如果使用GCD,以上3个方法都可以放到一起,如下所示:
// 原代码块一 self.indicator.hidden = NO; [self.indicator startAnimating]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 原代码块二 NSURL * url = [NSURL URLWithString:@"http://www.youdao.com"]; NSError * error; NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error]; if (data != nil) { // 原代码块三 dispatch_async(dispatch_get_main_queue(), ^{ [self.indicator stopAnimating]; self.indicator.hidden = YES; self.content.text = data; }); } else { NSLog(@"error when download:%@", error); } });
五、系统提供的dispatch方法
乍一看,gcd的语法貌似很复杂,其实很简单,苹果为了方便我们使用GCD,提供了包括后台执行、主线程执行、延后执行等一系列方法具体如下:
// 后台执行: dispatch_async(dispatch_get_global_queue(0, 0), ^{ // something }); // 主线程执行: dispatch_async(dispatch_get_main_queue(), ^{ // something }); // 一次性执行: static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // code to be executed once }); // 延迟2秒执行: double delayInSeconds = 2.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ // code to be executed on the main queue after delay });
dispatch_queue_t 也可以自己定义,如要要自定义queue,可以用dispatch_queue_create方法,示例如下:
dispatch_queue_t urls_queue = dispatch_queue_create("blog.devtang.com", NULL); dispatch_async(urls_queue, ^{ // your code }); dispatch_release(urls_queue);
两个线程并行
并行队列是不允许自己创建的,系统中存在三个不同优先级的并行队列。并行队列依旧按照任务添加的顺序启动任务,但是,后一个任务无须等待前一个任务执行完毕,而是启动第一个任务后,立即启动下一个任务。至于同一时刻允许同时运行多少个任务有系统决定。任务各自运行在并行队列为他们提供的独立线程上,并行队列中同时运行多少个任务,就必须维护多少个线程。
UInt32 loopCount = 1000; UInt32 loopCountFirst = 10000000; void (^taskFirst)(void) = ^{ NSLog(@"taskFirst 任务开始执行\r\n"); //延长taskFirst的运行时间 for (UInt32 i = 0; i < loopCountFirst; i++) { } NSLog(@"taskFirst 任务结束\r\n"); }; void (^taskSecond)(void) = ^{ NSLog(@"taskSecond任务开始执行\r\n"); for (UInt32 i = 0; i < loopCount; i ++) { } NSLog(@"taskSecond 任务结束\r\n"); }; dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(concurrentQueue, taskFirst); NSLog(@"taskfirst 已经加入队列\r\n"); dispatch_async(concurrentQueue, taskSecond); NSLog(@"tasksecond 已经加入队列\r\n");
另外,GCD还有一些高级用法,例如让后台2个线程并行执行,然后等2个线程都结束后,再汇总执行结果。这个可以用dispatch_group, dispatch_group_async 和 dispatch_group_notify来实现,示例如下:
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{ // 并行执行的线程一 }); dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{ // 并行执行的线程二 }); dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{ // 汇总结果 });
六、总结
GCD极大地方便了iOS开发者使用多线程来完成数据与UI的交互,且充分利用了当今处理器的多核功能,既提高了效率又方便了使用。
非常感谢唐巧的一篇gcd使用的文章,此文中有些内容本人觉得无法比唐巧写得好变摘录过来,敬请谅解!