【Objective-C】Block介绍

本文部分内容摘自

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句法解释。

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.BlockDelegateDemo

最后,如果你想比较一下BlockDelegate实现回调的异同,这里有我的一个简单Demo


  1. 在计算机科学中,如果一个编程语言把函数当作一等公民,那么就说它有一等函数。具体来说,该语言支持把函数当作一个参数来传递、可以作为其他函数的返回值来返回、可以把它定义为变量,同时,也可以把它作为一种数据结构来存储。(翻译自维基百科·First-class funcion)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,667评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,361评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,700评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,027评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,988评论 5 361
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,230评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,705评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,366评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,496评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,405评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,453评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,725评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,803评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,015评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,514评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,111评论 2 341

推荐阅读更多精彩内容

  • 禅与 Objective-C 编程艺术 (Zen and the Art of the Objective-C C...
    GrayLand阅读 1,597评论 1 10
  • 前言 Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这...
    小人不才阅读 3,751评论 0 23
  • 在前两篇中,我们介绍了一些关于C语言的重要概念,指针跟struct,这些基础知识是我们深入学习block的前提,在...
    ccSundayChina阅读 515评论 2 3
  • 我向天空大声地质问青春是什么颜色的 它用一条彩虹告诉我 青春是五颜六色的 我向大海大声地质问青春是什么声音的 它用...
    杨安劲阅读 227评论 0 2
  • 两个人在一起,相处和陪伴很重要,但如果贴太紧,却会让对方喘不过气。要是分开的太远,又很容易疏离。所以恋爱的最好方式...
    笃学青衿阅读 104评论 0 1