GCD笔记

一、什么是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使用的文章,此文中有些内容本人觉得无法比唐巧写得好变摘录过来,敬请谅解!

posted @ 2013-07-09 10:58  爱笑的雷小豆  阅读(2363)  评论(0编辑  收藏  举报