现在的位置: 首页 > 综合 > 正文

[Cocoa]深入浅出 Cocoa 多线程编程之 block 与 dispatch quene

2013年11月08日 ⁄ 综合 ⁄ 共 2876字 ⁄ 字号 评论关闭

block 是 Apple 在 GCC 4.2 中扩充的新语法特性,其目的是支持多核并行编程。我们可以将 dispatch_queue 与 block 结合起来使用,方便进行多线程编程。

本文源代码下载:点击下载

1,实验工程准备
在 XCode 4.0 中,我们建立一个 Mac OS X Application 类型的 Command Line Tool,在 Type 里面我们选择 Foundation 就好,工程名字暂且为 StudyBlocks.默认生成的工程代码 main.m 内容如下:

  1. int main (int argc, const char * argv[])  
  2. {  
  3.     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];  
  4.   
  5.     // insert code here...  
  6.     NSLog(@"Hello, World!");  
  7.   
  8.     [pool drain];  
  9.     return 0;  
  10. }  


2,如何编写 block
在自动生成的工程代码中,默认打印一条语句"Hello, World!",这个任务可以不可以用 block 语法来实现呢?答案是肯定的,请看:

  1. void (^aBlock)(void) = ^(void){ NSLog(@"Hello, World!"); };  
  2. aBlock();  

用上面的这两行语句替换 main.m 中的 NSLog(@"Hello, World!"); 语句,编译运行,结果是一样的。

这两行语句是什么意思呢?首先,等号左边的 void (^aBlock)(void) 表示声明了一个 block,这个 block 不带参数(void)且也无返回参数(void);等号右边的 ^(void){ } 结构表示一个 block 的实现体,至于这个 block 具体要做的事情就都在 {} 之间了。在这里我们仅仅是打印一条语句。整个语句就是声明一个
block,并对其赋值。第二个语句就是调用这个 block 做实际的事情,就像我们调用函数一样。block 很有点像 C++0X 中的 Lambda 表达式。


我们也可以这么写:

  1. void (^aBlock)(void) = 0;  
  2. aBlock = ^(void){  
  3.     NSLog(@" >> Hello, World!");  
  4. };  
  5. aBlock();  


现在我们知道了一个 block 该如何编写了,那么 block 数组呢?也很简单,请看:

  1. void (^blocks[2])(void) = {  
  2.     ^(void){ NSLog(@" >> This is block 1!"); },  
  3.     ^(void){ NSLog(@" >> This is block 2!"); }  
  4. };  
  5.   
  6. blocks[0]();  
  7. blocks[1]();  


谨记!
block 是分配在 stack 上的,这意味着我们必须小心里处理 block 的生命周期。
比如如下的做法是不对的,因为 stack 分配的 block 在 if 或 else 内是有效的,但是到大括号 } 退出时就可能无效了:

  1. dispatch_block_t block;  
  2.   
  3. if (x) {  
  4.     block = ^{ printf("true\n"); };  
  5. else {  
  6.     block = ^{ printf("false\n"); };  
  7. }  
  8. block();  


上面的代码就相当于下面这样的 unsafe 代码:

  1. if (x) {  
  2.     struct Block __tmp_1 = ...; // setup details  
  3.     block = &__tmp_1;  
  4. else {  
  5.     struct Block __tmp_2 = ...; // setup details  
  6.     block = &__tmp_2;  
  7. }  


3,如何在 block 中修改外部变量
考虑到 block 的目的是为了支持并行编程,对于普通的 local 变量,我们就不能在 block 里面随意修改(原因很简单,block 可以被多个线程并行运行,会有问题的),而且如果你在 block 中修改普通的 local 变量,编译器也会报错。那么该如何修改外部变量呢?有两种办法,第一种是可以修改 static 全局变量;第二种是可以修改用新关键字
__block 修饰的变量。请看:

  1. __block int blockLocal  = 100;  
  2. static int staticLocal  = 100;  
  3.   
  4. void (^aBlock)(void) = ^(void){   
  5.     NSLog(@" >> Sum: %d\n", global + staticLocal);  
  6.       
  7.     global++;  
  8.     blockLocal++;  
  9.     staticLocal++;  
  10. };  
  11.   
  12. aBlock();  
  13.   
  14. NSLog(@"After modified, global: %d, block local: %d, static local: %d\n", global, blockLocal, staticLocal);  


相似的情况,我们也可以引用 static block 或 __block block。比如我们可以用他们来实现 block 递归:

  1. // 1  
  2. void (^aBlock)(int) = 0;  
  3. static void (^ const staticBlock)(int) = ^(int i) {  
  4.     if (i > 0) {  
  5.         NSLog(@" >> static %d", i);  
  6.         staticBlock(i - 1);  
  7.     }  
  8. };  
  9.   
  10. aBlock = staticBlock;  
  11. aBlock(5);  
  12.   
  13. // 2  
  14. __block void (^blockBlock)(int);  
  15. blockBlock = ^(int i) {  
  16.     if (i > 0) {  
  17.         NSLog(@" >> block %d", i);  
  18.         blockBlock(i - 1);  
  19.     }  
  20. };  
  21.   
  22. blockBlock(5);  


4,上面我们介绍了 block 及其基本用法,但还没有涉及并行编程。 block 与 Dispatch Queue 分发队列结合起来使用,是 iOS 中并行编程的利器。请看代码:

抱歉!评论已关闭.