本文部分内容摘自。
1.Block
是啥?凭什么这么屌?
就本质来说,一个Block就是一大堆在接下来某个时间可以被执行的代码。
Block 是一等函数(first-class function)[1]。一等函数!这个梦幻般的爵位(如果计算机科学也有爵位的话~),却揭露了它丫的就是Objective-C的一个常规对象。因为它也就是个对象,所以它可以作为参数被传来传去,被方法和函数作为返回值送人,也可以被指定为变量。
Block在一些诸如Python、Ruby、Lisp这样的语言中被叫做闭包
,因为在它被声明的时候,它肚子里就已然有货了。一个block会为它范围内的任一变量做个copy,让小弟吃饱穿暖,别稀里糊涂的生命周期就结束了。
在这货出现之前,你想完成回调,最典型的就是用delegate或者NSNotificationCenter。这两个家伙表现挺好的,不过,却让你的代码到处跑,你从一个地方开始任务,却要在另一个地方处理结果。
Block就机灵得多,在处理任务的时候,它让你不挪窝,就把事儿给办了,就像你接下来将要看到的。
2.Block
为谁而生?
就是你们!不骗你们,Block是位每一个苦逼程序猿创造的,每个人都该用。Block代表了未来,所以你现在就得学。很多内置框架都基于这货重写或扩展了。
3.Block
怎么用?
下面那张妖里妖气的图片,来自苹果开发者库,做了很好的Block句法解释。
我从左到右,从上到下翻译一下:
- 我们声明了一个叫“myBlock”的变量,“^”告诉我们这是一个block;
- 这是一个字面量block的定义,指派给了“myBlock”;
- "myBlock"是一个返回整型值的block;
- 它只有一个整型参数;
- 参数名字叫“num”;
- 这是block的货,也就是block的实现。
Block的声明形式如下:
return_type (^block_name)(param_type, param_type, ...)
如果你有C系语言的编程经验的话,这会让你似曾相识,除了^
,^
表明“这货正在声明一个block”。
如果你脑海中萦绕着:^
表示“我是一个block”,恭喜——你已经学会使用block最难的部分。(我读书少,你可不要骗我啊...)
注意,在声明的时候,给参数命名不是必要的(只要知道是什么类型就行了),如果你愿意可以加上。
以下是声明一个block的例子:
int (^add)(int,int)
接着,是block的定义:
// Block Definition
^return_type(param_type param_name, param_type param_name, ...) { ... return return_type; }
这就是Block的创建形式。要注意,Block的定义和声明有不同的结构。定义是以^
开头,后面紧跟着参数,这次参数要有名字啦,并且它们的顺序要和Block定义中的一样。
当定义Block的时候,返回值类型可以不填,因为它可以根据代码推断出来。如果有多个返回的语句,那每个语句返回的必须是同一类型的(或者强转成相同的类型)。
以下是一个Block定义的例子:
^(int number1, int number2){ return number1+number2 }
如果我们把Block声明和定义的例子组合在一块儿,我们得到一个完整的语句:
int (^add)(int,int) = ^(int number1, int number2){
return number1+number2;
}
然后我们可以这样使用:
int resultFromBlock = add(2,2);
接下来,我们让用了Block的,和没用Block的代码PK一下!
例子: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来停止进一步的处理。
上面的例子有点不知所云,而且很难看出Block有嘛好处(擦~,那你为什么这么写...)。但是,有两点是我要指出的:
- 简单粗暴。用Block,我们有机会操作数组里的对象,对象的索引,和一个布尔变量,不用谢任何代码。而更少的代码以为着更少的错误。
- 唯快不破。用Block比快速枚举要有哦轻微的速度上的优势。在此例中,这速度上的优势(可能存在)是如此之小根本没法发觉,但在复杂的案例中,这个优势是很重要的(请看官们停止爆粗口吧,作者写到这里,可能也有点惴惴不安,所以给自己提供了一个证据,来证明自己不是大忽悠...)。
例子:UIView 动画
让我们操作一个UIView的简单动画。这个动画就是:把一个UIView从superview上删掉的时候,让它的透明度变为0,并且x、y各增加50。简单不?
不用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];
}];
}
观察这两个方法,对我来说,Block有3个突出的优点:
- 代码简洁。用Block,我们不必声明每一个单独的方法来完成回调。
- 代码集中。用Block,我们不用在一个地方开始动画,在另一个地方执行回调,所有与动画相关的,都在一起,使代码更加可读可写。
- 苹果大哥这么说的...这就是一个苹果用Block重写已经存在的功能的例子。现在苹果官方建议每位过渡到基于Block的方法(证据)。
4.Block
什么时候用?
(谁说外国人不会说官话,请欣赏,咳咳,难道是华裔)
我认为我能给出的最好的建议是,当你觉得合适的时候,你就该用了(hehe~)。很有可能你还想用你以前的方法,因为代码维护啊、适配啊,你用以前的方法更得心应手。但是每次你都要想想,是否Block能简化你的生活,是否可以用基于Block的方法来代替。然后选择最优的。
当然,你会发现你在将来需要用越来越多的Block,因为很多框架,无论是第三方的,或者苹果自己的,已经用Block写了或者重写了很多方法。所以现在开始就用Block吧,这样在面对未来的时候,你才能算有备而来(一句话,形势比人强,程序猿,看着办吧)。
5.创建自己的Block
Block的好,已经说半天了,你肯定已经迫不及待的想写一些Block了,下面给一些代码片段:
// 这是一个用Block做参数的方法
- (void)doMathWithBlock:(int (^)(int, int))mathBlock {
self.label.text = [NSString stringWithFormat:@"%d", mathBlock(3, 5)];
}
// 调用上面的方法
- (IBAction)buttonTapped:(id)sender {
[self doMathWithBlock:^(int a, int b) {
return a + b;
}];
}
因为Block就是一个Objective-C的对象,所以你可以把它作为一个属性,以便你再调用。这在回调中相当有用。以下是一个例子:
// 声明一个属性
@property (strong) int (^mathBlock)(int, int); // 不用ARC的话,“strong”改为“copy”
// 将方法参数中的block储存到属性,方便以后调用
- (void)doMathWithBlock:(int (^)(int, int))mathBlock {
self.mathBlock = mathBlock;
}
// 调用方法
- (IBAction)buttonTapped:(id)sender {
[self doMathWithBlock:^(int a, int b) {
return a + b;
}];
}
// 接下来就可以用属性了...
- (IBAction)button2Tapped:(id)sender {
self.label.text = [NSString stringWithFormat:@"%d", self.mathBlock(3, 5)];
}
最后,你可以用typedef
简化一下句法。我们把上面的例子改造一下:
// 用typedef创建一种Block类型
typedef int (^MathBlock)(int, int);
// 用此类型定义一个属性
@property (strong) MathBlock mathBlock;
// 将方法参数中的block储存到属性,方便以后调用
- (void)doMathWithBlock:(MathBlock) mathBlock {
self.mathBlock = mathBlock;
}
// 调用方法
- (IBAction)buttonTapped:(id)sender {
[self doMathWithBlock:^(int a, int b) {
return a + b;
}];
}
// 接下来就可以用属性了...
- (IBAction)button2Tapped:(id)sender {
self.label.text = [NSString stringWithFormat:@"%d", self.mathBlock(3, 5)];
}
6.Block
和Delegate
Demo
最后,如果你想比较一下Block
和Delegate
实现回调的异同,这里有我的一个简单Demo。
-
在计算机科学中,如果一个编程语言把函数当作一等公民,那么就说它有一等函数。具体来说,该语言支持把函数当作一个参数来传递、可以作为其他函数的返回值来返回、可以把它定义为变量,同时,也可以把它作为一种数据结构来存储。(翻译自维基百科·
First-class funcion
) ↩
网友评论