GCD的使用

一. GCD 是Grand Central Dispatch 的缩写

GCD是Apple开发的一个多核编程的解决方法。GCD是一个代替NSThread,NSOperator技术的高效和强大的技术。

GCD 和block 的配合使用,可以方便的进行多线程编程。

二.应用举例

让我们啦看一个编程场景。我们在iPhone上做一个下载的功能,该功能非常简单,就是在iPhone上放置一个按钮,当我们点击按钮的时候开始惊进行下载,下载完成之后,件内容显示在控件上面。

三.不用GCD前

虽然功能简单,但是我们必须把下载过程放到后台线程中,否则会阻塞UI线程显示。所以,如果不用GCD,我们需要些如下3个方法:

(1). someClick 方法是点击按钮后的代码,我么可以看到我们用NSInvocationOperation 建立一个后台线程,并且放到NSOperationQueue 中。后台线程执行downLoad方法。

(2).download方法处理下载网页的逻辑。下载完成后调用performSelectorOnMainThread执行download_completed 方法。

(3).download_completed 进行clear up的工作,并把下载的内容显示到文本控件上来。


这3个方法的代码如下。可以看到,虽然开始下载——》下载中——》下载完后这3个步骤是整个功能的三步。但是他们却被切分成3块,他们之间是3个方法,所以还需要传递数据参数,另外,下载可能放到Model的类中来做,而界面的控制放到ViewController层来做,这使得本来就分开的代码变得更加散落。代码的可读性大大降低。

static NSOperationQueue *queue;

-(IBAction)someClick:(id)sender

{

  self.indicator.hidden=no;

 [self.indicator startAnimating];

queue=[[NSOperatorQueue 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_complement:) withObject:data waitUntilDone:NO];

    }else{

        NSLog(@"error when download:%@", error);

    }

}

-(void)downLoad_complement:(NSString *)data

{

     NSLog(@"call back");

    [self.indicator stopAnimating];

    self.indicator.hidden = YES;

    self.content.text = data;

    NSLog(@"==data=%@",data);

}

使用GCD后,以上的3个方法都可以放到一起,如下所示

 

 //代码块1

 self.indicator.hidden=no;

 [self.indicator startAnimating];

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

       //代码块2

      NSURL *url=[NSURL URLWithString:@"http://www.youdao.com"];

      NSError *error;

      NSString *data=[NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];

      if(data!=nil){

        //代码会3

          dispatch_async(dispatch_get_main_queue(), ^{

              [self.indicator stopAnimating];

              self.indicator.hidden = YES;

              self.content.text=data;

              NSLog(@"===data==%@",data);

          });

      }else{

          NSLog(@"error when download:%@", error);

      }

  });

首先我们可以看到,代码变短了。因为少了原来 3 个方法的定义,也少了相互之间需要传递的变量的封装。

另外,代码变清楚了,虽然是异步的代码,但是它们被 GCD 合理的整合在一起,逻辑非常清晰。如果应用上 MVC 模式,我们也可以将 View Controller 层的回调函数用 GCD 的方式传递给 Modal 层,这相比以前用 @selector 的方式,代码的逻辑关系会更加清楚。

block的定义

block的定义有点像函数指针,差别是永^代替了*号

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

dispatch_async(dispatch_get_global_queue(0,0),^{

// someThing

});

从上面大家可以看出,block有如下特点:

1.程序块可以再代码中以内联的方式定义。

2.程序块可以访问在创建他的范围内的可用变量。

系统提供的 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);

    dispatch_after(popTime,dispatch_get_main_queue(), ^{

        //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_group_t group=dispatch_group_create();

    dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

        //并行执行的线程一

    });

    dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

        //并行执行的线程二

    });

    

    dispatch_group_notify(group,dispatch_get_main_queue(), ^{

        //汇总结果

    });

    

    

   // 修改 block 之外的变量

    

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


    __block int a=0;

    void (^foo)(void)=^{

        a=1;

        

    };

    

    foo();

    // 这里,a的值被修改为1

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

    

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

在APPDelegate.h 文件中

@property (nonatomic,assign) 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 蜡处理后台线程和UI线程的交互。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值