【Objective-C】Block介绍

作者: 刘大帅 | 来源:发表于2014-12-10 01:10 被阅读884次

    本文部分内容摘自

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

    blockblock

    我从左到右,从上到下翻译一下:

    • 我们声明了一个叫“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)

    相关文章

      网友评论

        本文标题:【Objective-C】Block介绍

        本文链接:https://www.haomeiwen.com/subject/qbentttx.html