iOS如何使用Block

本文转载于 http://segmentfault.com/a/1190000003093017, 如有侵权,请原著与我沟通,可以立即取消发布。

什么是Block


Blocks 是 iOS 4.0 之后有的新功能。
Block 能够让我们的代码变得更简单,能够减少代码量,降低对于 delegate 的依赖,还能够提高代码的可读性。

本质上来说,一个 Block 就是一段能够在将来被执行的代码。本身 Block 就是一个普通的 Objective-C 对象。正因为它是对象,Block 可以被作为参数传递,可以作为返回值从一个方法返回,可以用来给变量赋值。

在其他语言(Python, Ruby, Lisp etc.)中,Block 被叫做闭包——因为他们在被声明的时候的封装状态。Block 为指向它内部的局部变量创造了一个常量 copy。

在 Block 之前,如果我们想要调用一段代码,然后之后一段时间,让它给我们返回,我们一般会使用 delegate 或者 NSNotification。这样的写法没什么问题,但是使用过 delegate 和 NSNotification 大家就应该会感觉到——我们会不可避免的将代码写的到处都是,我们需要在某处开始一个任务,在另外一个地方来处理这个返回结果。使用 Block 就可以在一定程度上避免这个问题。

如何使用 Block


下面这张图片来自苹果官方文档:

block实例

Block 的声明格式如下

return_type (^block_name)(param_type, param_type, ...

^ 符号其实就是专门用来表示:我们在声明一个 Block。

声明举例:

int (^add)(int,int)

block 的定义格式如下

^return_type(param_type param_name, param_type param_name, ...)
 { ... return return_type; }

要注意,定义和声明的格式有一些不同。定义以 ^ 开始,后面跟着参数(参数在这里一定要命名),顺序和类型一定要和声明中的顺序一样。定义时,返回值类型是 optional 的,我们可以在后面的代码中确定返回值类型。如果有多个返回 statement,他们也只能有一个返回值类型,或者把他们转成同一个类型。block 的定义举例:

 ^(int number1, int number2){ return number1+number2 }

我们把声明和定义放在一起:

int (^add)(int,int) = ^(int number1, int number2){ return number1+number2;}

调用的时候

int resultFromBlock = add(2,2);

我们将使用 block 与不使用 block 做一些对比

举例 :NSArray
普通 for 循环:

BOOL stop;
for (int i = 0 ; i < [theArray count] ; i++) {
NSLog(@"The object at index %d is %@",i,[theArray objectAtIndex:i]);
if (stop)
    break;
}

这个 BOOL 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);
}];

在上面的代码中, BOOL stop 设置为 YES 的时候,可以从block 内部停止下一步运行。
从上面三段代码的对比中,我们可以至少可以看出 block 两方面的优势:

  • 简化了代码
  • 提高了速度

举例:UIView Animation

非 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 的优势:简化了代码

让代码保持在一起,不需要在一个地方开始动画,在另一个地方回调。读写起来都比较方便。苹果也建议这么做,不然苹果用 block 重写以前的代码干嘛呢~

block 的应用


1. enumerateObjectsUsingBlock

之前的代码实例中已经提到过,用来迭代数组十分方便,具体看下面的代码实例:

-(NSArray*)retrieveInventoryItems {
    // 1 - 声明
    NSMutableArray* inventory = [NSMutableArray new];
    NSError* err = nil;
    // 2 - 得到 inventory 数据
    NSArray* jsonInventory = [NSJSONSerialization JSONObjectWithData:
                             [NSData dataWithContentsOfURL:[NSURL URLWithString:kInventoryAddress]] 
                              options:kNilOptions 
                              error:&err];
    // 3 - 使用 block 遍历
    [jsonInventory enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSDictionary* item = obj;
        [inventory addObject:[[IODItem alloc] 
                             initWithName:[item objectForKey:@"Name"] 
                             andPrice:[[item objectForKey:@"Price"] floatValue]
                             andPictureFile:[item objectForKey:@"Image"]]];
    }];
    // 4 - 返回一个 inventory 的 copy
    return [inventory copy];
}

我们在上面的代码中 3 处使用了 block:

使用了 enumerateObjectsUsingBlock 方法,把一个普通的 NSDictionary 转化成一个 IODItem 类的对象。我们对一个JSON Array 对象发送 enumerateObjectsUsingBlock 消息,迭代这个 array,得到 item 字典,然后用这个字典得到 IODItem,最后把这些对象添加到 inventory 数组然后返回。

2.sortedArrayUsingComparator

enumerateObjectsUsingBlock我们上面已经用过,主要来看 sortedArrayUsingComparator ,这个 block 以一个升序返回一个 array,这个升序由一个 NSComparator block 决定

注意:compare 方法的使用有点没太明白,但是根据 sortedArrayUsingComparator 是返回一个升序数组,所以compare 方法应该是返回两者之间更大的??


-(NSString*)orderDescription {
        // 1 - 声明
    NSMutableString* orderDescription = [NSMutableString new];
        // 2 - 使用 block 进行排序
    NSArray* keys = [[self.orderItems allKeys] sortedArrayUsingComparator:
       ^NSComparisonResult(id obj1, id obj2) {
        IODItem* item1 = (IODItem*)obj1;
        IODItem* item2 = (IODItem*)obj2;
        return [item1.name compare:item2.name];
    }];
        // 3 - 使用 block 遍历
    [keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        IODItem* item = (IODItem*)obj;
        NSNumber* quantity = (NSNumber*)[self.orderItems objectForKey:item];
        [orderDescription appendFormat:@"%@ x%@\n", item.name, quantity];
    }];
        // 4 - 返回
    return [orderDescription copy];
}

注释2:得到一个包含 dictionary 中所有 key 的数组,然后使用 sortedArrayUsingComparator 这个 block 方法,把这些所有的 key 按升序进行排序。

总结一些比较常用的 block


NSArray

  1. enumerateObjectsUsingBlock 这个是我最常使用的 block ,上面已经介绍过了,用来迭代数组非常方便,个人认为这应该是最好用的 block 了。
  2. enumerateObjectsAtIndexes:usingBlock: 和 enumerateObjectsUsingBlock 差不多,但是我们可以选择只迭代数组的一部分,而不是迭代整个数组。这个需要迭代的范围由 indexSet 参数传入。
  3. indexesOfObjectsPassingTest 返回一个数组中,通过了特定的 test 的对象的 indexSet。用这个 block 来查找特定的数据很方便。

NSDictionary

  1. enumerateKeysAndObjectsUsingBlock 迭代整个字典,返回字典中所有的 key 和对应的值(如果是想用这个 block 来代替 objectForKey 方法,确实有些多此一举,但是如果你需要返回字典中全部的 key, value,这个 block 是一个很好的选择)。
  2. keysOfEntriesPassingTest 和数组里的 indexesOfObjectsPassingTest block 类似,返回通过特定的 test 的一组对象的 key。

UIView Animation

animateWithDuration: animation: completion: 写过动画大家应该还是比较了解的,动画的 block 实现确实比非 block 实现简单、方便了很多。

GCD

dispatch async:这时异步 GCD 的主要功能,在这里其实最重要的是要理解 block 是一个普通的对象,是可以作为参数传递给 dispatch queue 的。

使用我们自己的 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 对象,所以我们可以把 block 存储在一个 property 中以便之后调用。这种方式在处理异步任务的时候特别有用,我们可以在一个异步任务完成之后存储一个 block,之后可以调用。下面是一段示例代码:

@property (strong) int (^mathBlock)(int, int);

// 存储 block 以便之后调用
-(void)doMathWithBlock:(int (^)(int, int))mathBlock {
    self.mathBlock = mathBlock;
}
 
// 调用上面的方法,并传入一个 block
-(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 来简化block 语法,当然效果和上面的是差不多的,我们来看下面的例子:

typedef int (^MathBlock)(int, int);
 
// 使用 tpyedef 声明一个property
@property (strong) MathBlock mathBlock;
 
-(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)];
}

参考链接

Bingo !

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

推荐阅读更多精彩内容

  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,204评论 0 4
  • iOS代码块Block 概述 代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,B...
    smile刺客阅读 2,328评论 2 26
  • 剽悍晨读:如何化问题为机遇,巧妙突破人生障碍? 这本书的精髓,我觉得就是题目了,书名就是精髓《人生总有办法》! 自...
    小秦哥哥阅读 432评论 2 4
  • 1 许多年以后,面对广电总局的一纸禁令,砰砰博士将会想起,他的父亲带他去见识炉火的那个遥远的下午。当时,布利泽德是...
    行虑阅读 432评论 6 4
  • 庙堂高居意惶惶,英雄何必沽帝皇。 笼中宠失铩羽翅,悔不山庄牧牛羊。
    蔚海山庄三六子阅读 153评论 1 7