GCD的使用

来源:互联网 发布:高清网络播放器排名 编辑:程序博客网 时间:2024/06/01 18:20

一. 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=[NSURLURLWithString:@"http://www.youdao.com"];

    NSError *error;

    NSString *data=[NSStringstringWithContentsOfURL:url encoding:NSUTF8StringEncodingerror:&error];

    if(data!=nil){

        [selfperformSelectorOnMainThread:@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=[NSURLURLWithString:@"http://www.youdao.com"];

      NSError *error;

      NSString *data=[NSStringstringWithContentsOfURL:url encoding:NSUTF8StringEncodingerror:&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

    });


    //一次性执行

    staticdispatch_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来让其写操作生效,示例代码如下:


    __blockint a=0;

    void (^foo)(void)=^{

        a=1;

        

    };

    

    foo();

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

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

    

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

在APPDelegate.h 文件中

@property (nonatomic,assign)UIBackgroundTaskIdentifier backgroundUpdateTask;


AppDelegate.m  文件中

- (void)applicationDidEnterBackground:(UIApplication *)application

{

    [self beingBackgroundUpdateTask];   

    //在这里加上你需要长久运行的代码

    [selfendBackgroundUpdateTask];

}

- (void)beingBackgroundUpdateTask

{

    self.backgroundUpdateTask = [[UIApplicationsharedApplication] beginBackgroundTaskWithExpirationHandler:^{

        [selfendBackgroundUpdateTask];

    }];

}


- (void)endBackgroundUpdateTask

{

    [[UIApplicationsharedApplication] endBackgroundTask:self.backgroundUpdateTask];

    self.backgroundUpdateTask =UIBackgroundTaskInvalid;

}

大家尽量的使用GCD 蜡处理后台线程和UI线程的交互。


0 0
原创粉丝点击