Block事实上是一个代码块, 可以将它简单的看作是个函数, 但它能被赋给一个变量. 它在xcode中被描述成 ^ (args...) { expression... }
一个简单的例子:
int (^add)(int, int) = ^(int a, int b) { return a + b; };
int x = add(1,2);
运行完之后, 变量x的值为 3
有点意思吧, 这个看上去和传统c语言中的函数指针有点类似, 但事实上Block更为灵活. 为什么呢? 细心的人可能会发现,因为Block语句是在一个函数中定义的语句块, 是不是意味着具有和当前函数相同的变量操作的作用域呢? 对,的确如此!
还是看例子吧:
int a = 10;
int (^block1)(int) = ^ (int b) { return a + b; };
int x = block1(3);
运行之后,变量x的值为 13
可能你会觉得有点晕? 或者你会想问...如果在block 之外对这个变量进行修改了,结果会如何?
让我们稍微修改下以前的代码:
int a = 10;
int (^block1)(int) = ^ (int b) { return a + b; };
a = 3;
int x = block1(3);
运行后x 的结果依然是 13
事实上,在 Block主体用到这个变量的时候,将做一个copy, 将a当时的值复制下来, 之后a的改动将不再影响到block中的语句的执行了. 如果你用过java, 应该记得如果要在inner函数中使用外部的变量,必须将这个变量加上 final的修饰符,也是类似的道理.
更多的问题又来了. 你会问: 既然是做了个copy,那如果想改变Block外部的变量的值,那不是不可能了么?
对,的确有这个问题,但事实上这套机制没有这么弱智。可以在外部变量前加上一个修饰符 __block, 表示这个变量是一个 block variable. 这样就可以在Block块中任意读写这个变量,而它的数值在外面被改动了也能反映到Block中来。
又是例子:
__block int a = 10;
int (^block1)(int) = ^ (int b) { return a + b; };
a = 3;
int x = block1(3);
运行后x的值为 6
很有趣吧?
其实在ios和apple开发中还是经常能利用这个方便的特性,比如我常爱用blocks方法来实现不同线程中的切换,有些场合要比NSThread和NSOperation灵活些
比如一个异步通信中,控件的属性和方法调用需要在UI线程中(在apple中是主线程)进行,而联网等耗时操作为了不对UI线程造成阻塞,往往需要在另外一个工作线程中运行。
我们可以简单的这么做:
- (IBAction)loginAction:(id)sender {
NSLog(@"ENTER: onLogin.");
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ // Show the progress bar in the main tread
dispatch_async(dispatch_get_main_queue(), ^{
[self.progressControl setMessage:@"通信中,请稍候..."];
[self.progressControl show];
});
// TODO: connect with server to send login request, then receive response.
[self loginSuccess];
// Hide the progress bar in the main tread
dispatch_async(dispatch_get_main_queue(), ^{
[self.progressControl dismiss];
});
});
}
当然你也可以用这么做,这也是较常用的做法
- (IBAction)loginAction:(id)sender {
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doLogin:) object:args];
[[BFGlobalOperationPool globalInstance]addQueueOperation:operation];
[operation release];
// Show the progress bar in the main tread
[self.progressControl setMessage:@"通信中,请稍候..."];
[self.progressControl show];
}
- (void) doLogin:(id)args {
// TODO: connect with server to send login request, then receive response.
[self loginSuccess];
// Hide the progress bar in the main tread
[self.progressControl performSelectorOnMainThread:@selector(dismiss) withObject:nil waitUntilDone:NO];
}
显而易见,用blocks的更省事些。