block 是 Apple 在 GCC 4.2 中扩充的新语法特性,其目的是支持多核并行编程。我们可以将 dispatch_queue 与 block 结合起来使用,方便进行多线程编程。
本文源代码下载:点击下载
1,实验工程准备
在 XCode 4.0 中,我们建立一个 Mac OS X Application 类型的 Command Line Tool,在 Type 里面我们选择 Foundation 就好,工程名字暂且为 StudyBlocks.默认生成的工程代码 main.m 内容如下:
- int main (int argc, const char * argv[])
- {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- // insert code here...
- NSLog(@"Hello, World!");
- [pool drain];
- return 0;
- }
2,如何编写 block
在自动生成的工程代码中,默认打印一条语句"Hello, World!",这个任务可以不可以用 block 语法来实现呢?答案是肯定的,请看:
- void (^aBlock)(void) = ^(void){ NSLog(@"Hello, World!"); };
- aBlock();
用上面的这两行语句替换 main.m 中的 NSLog(@"Hello, World!"); 语句,编译运行,结果是一样的。
这两行语句是什么意思呢?首先,等号左边的 void (^aBlock)(void) 表示声明了一个 block,这个 block 不带参数(void)且也无返回参数(void);等号右边的 ^(void){ } 结构表示一个 block 的实现体,至于这个 block 具体要做的事情就都在 {} 之间了。在这里我们仅仅是打印一条语句。整个语句就是声明一个
block,并对其赋值。第二个语句就是调用这个 block 做实际的事情,就像我们调用函数一样。block 很有点像 C++0X 中的 Lambda 表达式。
我们也可以这么写:
- void (^aBlock)(void) = 0;
- aBlock = ^(void){
- NSLog(@" >> Hello, World!");
- };
- aBlock();
现在我们知道了一个 block 该如何编写了,那么 block 数组呢?也很简单,请看:
- void (^blocks[2])(void) = {
- ^(void){ NSLog(@" >> This is block 1!"); },
- ^(void){ NSLog(@" >> This is block 2!"); }
- };
- blocks[0]();
- blocks[1]();
谨记!
block 是分配在 stack 上的,这意味着我们必须小心里处理 block 的生命周期。
比如如下的做法是不对的,因为 stack 分配的 block 在 if 或 else 内是有效的,但是到大括号 } 退出时就可能无效了:
- dispatch_block_t block;
- if (x) {
- block = ^{ printf("true\n"); };
- } else {
- block = ^{ printf("false\n"); };
- }
- block();
上面的代码就相当于下面这样的 unsafe 代码:
- if (x) {
- struct Block __tmp_1 = ...; // setup details
- block = &__tmp_1;
- } else {
- struct Block __tmp_2 = ...; // setup details
- block = &__tmp_2;
- }
3,如何在 block 中修改外部变量
考虑到 block 的目的是为了支持并行编程,对于普通的 local 变量,我们就不能在 block 里面随意修改(原因很简单,block 可以被多个线程并行运行,会有问题的),而且如果你在 block 中修改普通的 local 变量,编译器也会报错。那么该如何修改外部变量呢?有两种办法,第一种是可以修改 static 全局变量;第二种是可以修改用新关键字
__block 修饰的变量。请看:
- __block int blockLocal = 100;
- static int staticLocal = 100;
- void (^aBlock)(void) = ^(void){
- NSLog(@" >> Sum: %d\n", global + staticLocal);
- global++;
- blockLocal++;
- staticLocal++;
- };
- aBlock();
- NSLog(@"After modified, global: %d, block local: %d, static local: %d\n", global, blockLocal, staticLocal);
相似的情况,我们也可以引用 static block 或 __block block。比如我们可以用他们来实现 block 递归:
- // 1
- void (^aBlock)(int) = 0;
- static void (^ const staticBlock)(int) = ^(int i) {
- if (i > 0) {
- NSLog(@" >> static %d", i);
- staticBlock(i - 1);
- }
- };
- aBlock = staticBlock;
- aBlock(5);
- // 2
- __block void (^blockBlock)(int);
- blockBlock = ^(int i) {
- if (i > 0) {
- NSLog(@" >> block %d", i);
- blockBlock(i - 1);
- }
- };
- blockBlock(5);
4,上面我们介绍了 block 及其基本用法,但还没有涉及并行编程。 block 与 Dispatch Queue 分发队列结合起来使用,是 iOS 中并行编程的利器。请看代码: