GCD--自己的开发笔记

来源:互联网 发布:晨光麦事件 知乎 编辑:程序博客网 时间:2024/06/08 16:52

GCD

在object-c 和swift GCD语言都适用

例:

// 原代码块一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);    }});

一、block的定义

block的定义有点象函数指针,差别是用 ^ 替代了函数指针的 * 号,如下所示:

 // 申明变量 (void) (^loggerBlock)(void); // 定义 loggerBlock = ^{      NSLog(@"Hello world"); }; // 调用 loggerBlock();
大多数时候,我们通常使用内联的方式来定义block,即将它的程序块写在调用的函数里面,例如这样:

 dispatch_async(dispatch_get_global_queue(0, 0), ^{      // something });

二、系统提供的dispatch方法

为了方便地使用GCD,苹果提供了一些方法方便我们将block放在主线程 或 后台线程执行,或者延后执行。使用的例子如下:

 //  后台执行: 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);

另外,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), ^{      // 汇总结果 });


    // 根据url获取UIImage      - (UIImage *)imageWithURLString:(NSString *)urlString {          NSURL *url = [NSURL URLWithString:urlString];          NSData *data = [NSData dataWithContentsOfURL:url];          // 这里并没有自动释放UIImage对象          return [[UIImage alloc] initWithData:data];      }            - (void)downloadImages {          dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);                    // 异步下载图片          dispatch_async(queue, ^{              // 创建一个组              dispatch_group_t group = dispatch_group_create();                            __block UIImage *image1 = nil;              __block UIImage *image2 = nil;                            // 关联一个任务到group              dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{                  // 下载第一张图片                  NSString *url1 = @"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg";                  image1 = [self imageWithURLString:url1];              });                            // 关联一个任务到group              dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{                  // 下载第一张图片                  NSString *url2 = @"http://hiphotos.baidu.com/lvpics/pic/item/3a86813d1fa41768bba16746.jpg";                  image2 = [self imageWithURLString:url2];              });                            // 等待组中的任务执行完毕,回到主线程执行block回调              dispatch_group_notify(group, dispatch_get_main_queue(), ^{                  self.imageView1.image = image1;                  self.imageView2.image = image2;                                    // 千万不要在异步线程中自动释放UIImage,因为当异步线程结束,异步线程的自动释放池也会被销毁,那么UIImage也会被销毁                                    // 在这里释放图片资源                  [image1 release];                  [image2 release];              });                            // 释放group              dispatch_release(group);          });      }  


三、修改block之外的变量

默认情况下,在程序块中访问的外部变量是复制过去的,即写操作不对原变量生效。但是你可以加上 __block来让其写操作生效,示例代码如下:

__block int a = 0; void  (^foo)(void) = ^{      a = 1; } foo(); // 这里,a的值被修改为1

四、暂停、继续queue

我们可以使用dispatch_suspend函数暂停一个queue以阻止它执行block对象;

使用dispatch_resume函数继续dispatch queue。

调用dispatch_suspend会增加queue的引用计数,调用dispatch_resume则减少queue的引用计数。

当引用计数大于0时,queue就保持挂起状态。因此你必须对应地调用suspend和resume函数。

挂起和继续是异步的,而且只在执行block之间(比如在执行一个新的block之前或之后)生效。挂起一个queue不会导致正在执行的block停止。


五、后台运行


使用block的另一个用处是可以让程序在后台较长久的运行。在以前,当app被按home键退出后,app仅有最多5秒钟的时候做一些保存或清理资源的工作。但是应用可以调用UIApplication的beginBackgroundTaskWithExpirationHandler方法,让app最多有10分钟的时间在后台长久运行。这个时间可以用来做清理本地缓存,发送统计数据等工作。

让程序在后台长久运行的示例代码如下:

// AppDelegate.h文件@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundUpdateTask;// AppDelegate.m文件- (void)applicationDidEnterBackground:(UIApplication *)application{    [self beingBackgroundUpdateTask];    // 在这里加上你需要长久运行的代码    [self endBackgroundUpdateTask];}- (void)beingBackgroundUpdateTask{    self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{        [self endBackgroundUpdateTask];    }];}- (void)endBackgroundUpdateTask{    [[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];    self.backgroundUpdateTask = UIBackgroundTaskInvalid;}



总体来说,GCD能够极大地方便开发者进行多线程编程。个人现在都没有怎么用代理了,都用block了。








0 0