iOS 如何使用 Block

什么是 block

Blocks 是 iOS 4.0 以后有的新功能。
Block 可以让咱们的代码变得更简单,可以减小代码量,下降对于 delegate 的依赖,还可以提升代码的可读性。ios

本质上来讲,一个 Block 就是一段可以在未来被执行的代码。自己 Block 就是一个普通的 Objective-C 对象。正由于它是对象,Block 能够被做为参数传递,能够做为返回值从一个方法返回,能够用来给变量赋值。
在其余语言(Python, Ruby, Lisp etc.)中,Block 被叫作闭包——由于他们在被声明的时候的封装状态。Block 为指向它内部的局部变量创造了一个常量 copy。json

在 Block 以前,若是咱们想要调用一段代码,而后以后一段时间,让它给咱们返回,咱们通常会使用 delegate 或者 NSNotification。这样的写法没什么问题,可是使用过 delegate 和 NSNotification 你们就应该会感受到——咱们会不可避免的将代码写的处处都是,咱们须要在某处开始一个任务,在另一个地方来处理这个返回结果。使用 Block 就能够在必定程度上避免这个问题。数组

如何使用 Block

下面这张图片来自苹果官方文档:
blocks.jpg闭包

Block 的声明格式以下:app

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

^ 符号其实就是专门用来表示:咱们在声明一个 Block。
声明举例:异步

int (^add)(int,int)

block 的定义格式以下:async

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

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

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

咱们把声明和定义放在一块儿:spa

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

调用的时候:3d

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 按升序进行排序。

3.enumerateKeysAndObjectsUsingBlock

这个方法苹果的官方说明:将一个 block 对象使用在一个字典的 entries 上(Apply an given block object to the entries of dictionary),咱们来看一段实例代码:

-(float)totalOrder {
        // 1 - 定义
    __block float total = 0.0;
        // 2 - 使用 block 计算
    float (^itemTotal)(float,int) = ^float(float price, int quantity) {
        return price * quantity;
    };
        // 3 - 使用 block 遍历 dictionary
    [self.orderItems enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        IODItem* item = (IODItem*)key;
        NSNumber* quantity = (NSNumber*)obj;
        int intQuantity = [quantity intValue];
        total += itemTotal(item.price, intQuantity);
    }];
        // 4 - 返回
    return total;
}

注释1:须要注意的是 block,由于咱们须要在 block 内部使用这个 total 变量;若是咱们不使用 block 关键字,下面的 block 会建立一个这个 total 变量的常量拷贝(const copy),当指向 block 内部的时候,就使用这个拷贝。这意味着在 block 咱们没法修改它的值。但添加了这个关键字咱们就能够从 block 内部读取变量的值,或者把值写入 block 内的变量。

注释3:使用 enumerateKeysAndObjectsUsingBlock 能够遍历获得字典里全部的对象,以及它们对应的 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 !

关于 block 的使用说的差很少了,其实关于 block 最关键的是要理解,block 是能够做为参数和返回值使用得。接下来会总结一篇关于 GCD 的文章。

相关文章
相关标签/搜索