本帖最后由 黄铁军老师 于 2016-6-25 11:25 编辑
Block 是 iOS 4.0 和 Mac OSX 10.6 引入的一个新特性。 Block 可以极大的简化代码。 他们可以帮助你减少代码, 减少对代理的依赖, 并且写出更加简洁,可读性强的代码。 即使有这么多好处, 还是有很多开发者没有使用 Block, 因为他们不知道如何使用。 但是 Block 绝对是你作为一个 Objective-C 程序员,一定会想要掌握的技能。 让我们来看看 Block 是谁, 是什么,在哪里用它, 为什么用它, 还有什么时候用它。 Block 是什么东西,它为什么那么重要?
Block 的核心就是一段可以在以后的时间里执行的代码。 在其他语言中,比如 Python,Ruby 和 Lisp, Block 又叫做闭包, 因为他们包含了定义时的状态。 Block 会为所有和它在同一作用范围内的局部变量创建一个常量拷贝。 在没有 Block 之前, 如果我们想在之后的某个时间回调一个方法, 你一般会用代理或者 NSNotificationCenter。 这样也不错, 除了一点,它会让你的代码到处都是 – 你在一个地方开启了一个任务, 然后在另外一个地方处理它的结果。 Block 是非常不错的, 因为它能将和一个任务相关的所有代码都放在一个地方, 你马上就会看到。 Block 为谁准备? 你! Block 是为每一个人准备的! 严格的说, Block 是为每个人和每个将要用到 Block 的人准备的。 Block 是一个的趋势, 所以你最好现在也学一下。 很多内建的方法已经用 Block 重写或者提供了接受 Block 参数的版本。 Block 的声明格式如下: return_type (^block_name)(param_type, param_type, ...) |
如果你之前使用过其他 C 类型的语言,那这段代码你应该看起来很眼熟, 除了这个 ^ 符号。 ^ 这个符号表示了 “我们定义的这个东西是一个 Block”。 注意这里不需要参数的名称, 不过,如果你喜欢的话,你也可以加上它们。 下面是定义 Block 的一个例子:
下面是 Block 的定义格式: // Block Definition^return_type(param_type param_name, param_type param_name, ...) { ... return return_type; } |
这就是 Blcok 实际是怎么创建的。 Block 还有另外一种不同的定义方法。 以 ^ 符号起始,后面跟随着参数,这里的参数必须有参数名, 还必须和它要赋值到的 Block 声明中参数列表里面的参数类型和顺序相匹配。下面是实际的代码。 当你定义 Block 时, 返回值类型是可选的,并且可以继承它里面代码的返回值类型。 如果它里面有多条 return 语句,那么这些语句返回的类型必须都是相同的 (或者强制转换到相同的类型)。 这里是 Block 定义的一个例子: ^(int number1, int number2){ return number1+number2 } |
如果我们将 Block 的声明和定义放在一起, 我们会得到这样一个语句: int (^add)(int,int) = ^(int number1, int number2){ return number1+number2;} |
我们可以这样使用 Block: int resultFromBlock = add(2,2); |
让我们看一看,使用 Block 和不使用 Block 之间对比的一些例子。 示例: NSArray 让我们看看 Block 如何改变我们操作数组的方式。 首先,让我们看一下一般情况下处理循环的方式: BOOL stop;for (int i = 0 ; i < [theArray count] ; i++) { NSLog(@"The object at index %d is %@",i,[theArray objectAtIndex:i]); if (stop) break;} |
上面方法中的 “stop” 变量,可能会让你不太明白。 但是如果用 Block 的方式实现它,你就会很清楚的看明白了。 Block 提供了一个 “stop” 变量能让你在任何时候停止循环,我们简单的复制了这个功能来支持和 Block 的方式等同的效果。 现在,让我们看看用快速枚举的方法实现同样的功能: BOOL stop;int idx = 0;for (id obj in theArray) { NSLog(@"The object at index %d is %@",idx,obj); if (stop) break; idx++;} |
现在,用 Block: [theArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){ NSLog(@"The object at index %d is %@",idx,obj);}]; |
在上面这个基于 Block 的代码中,你可能会好奇 “stop” 这个变量到底是什么。 这个变量可以在 block 中赋值为 YES, 这样就后续的任何循环都不会继续了。 这是传递到 enumerateObjectsUsingBlock 方法的 Block 中的其中一个参数。 上面这个例子有些微不足道, 而且也很难明显的体现出 Block 所到来的好处。 但是我想给大家指出 Block 的两点好处: - 简单性. 使用 Block 我们可以不写任何附加的代码就可以访问对象,对象在数组中的索引,stop 变量。 这意味着少量的代码,减少了发生编码错误的机会(当然,并非我们一定会出现编码错误)。
- 速度. 使用 Block 在执行速度上要比使用快速枚举快。 在我们这个例子中,这点微小的速度提升不值得一提,但是在更复杂的情况下,这个优势就越来越重要。
示例: UIView Animation 让我们对一个单独的 UIView 执行一个简单的动画。 它将视图的透明度调整为 0,将这个视图向下和向右移动 50 点。 然后将这个 UIView 从它的 superview 中删除掉, 很简单,对吗? 非 Block 的实现方式: - (void)removeAnimationView:(id)sender {
[animatingView removeFromSuperview];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[UIView beginAnimations:@"Example" context:nil];
[UIView setAnimationDuration:5.0];
[UIView setAnimationDidStopSelector:@selector(removeAnimationView)];
[animatingView setAlpha:0];
[animatingView setCenter:CGPointMake(animatingView.center.x+50.0,animatingView.center.y+50.0)];
[UIView commitAnimations];
} |
Block 的实现方式: - (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[UIView animateWithDuration:5.0 animations:^{
[animatingView setAlpha:0];
[animatingView setCenter:CGPointMake(animatingView.center.x+50.0, animatingView.center.y+50.0)];
}completion:^(BOOL finished) {
[animatingView removeFromSuperview];
}];} |
如果我们仔细看一看这两个方法, 就会发现有 3 个优势: - 更简单的代码 使用 Block, 我们不再需要单独定义一个回调方法, 或者调用 beginAnimations/commitAnimations 。
- 保持代码在一起 使用 Block, 我们不再需要在一个地方开启动画,然后再另外一个地方处理回调。 所有和我们动画相关的代码都在一处, 这样让他的可读性和维护性更强。
- 苹果推荐这样 这里有一个现实的例子, 苹果已经用 Block 重写了之前的一些功能, 现在苹果官方也推荐,如果可能的话,尽量迁移到基于 Block 的方法上面。
什么时候用 Block 我认为最佳的建议是, 在最合适用 Block 的地方使用它。 这里你可能会出于向后兼容或者更加熟悉以前的方式的原因,从而还要用老的方法。
但是每次你面临这种决策的时候, 想一想 Block 是否能让你更轻松以及你是否能用基于 Block 的方式代替现有代码。 然后选择一个对你最有价值的方法。 当然,你可能会发现,在以后的时间里, 你需要越来越多的使用 Block, 因为大多数框架,无论是第三方的还是苹果自己的,都正在用 Block 重写。所以为了让你未来更加轻松,现在就开始使用 Block 吧。
|