IOS Block详解

来源:互联网 发布:golang 统计文章字数 编辑:程序博客网 时间:2024/04/28 17:59


 这是我原先写的OC中关于协议和代理的文章,建议大家阅读此篇文章的时候先阅读此文章,便于大家理解:   IOS Protocol与Delegate详解(一)  IOS Protocol与Delegate详解(二)


 官方中对于Block的用途为: 


 You can use blocks to compose function expressions that can be passed to API, optionally stored, and usedby multiple threads. Blocks are particularly useful as a callback because the block carries both the code to beexecuted on callback and the data needed during that execution. 


  你可以使用Blocks组成方法表达式组成API接口,可选择的存储,或者运用其在不同的线程中,Blocks在回调中非常的重要因为Blocks携带了需要回调的代码和执行时的参数

 

   从上面可以看出,

   Blocks最最重要的地方就是可以用来回调,准确的是,是用来方便的回调

   如果你看过我以前写的"Protocol与Delegate详解"的话,就会知道,Protocol(协议)可以看成是一个方法的集合,而block可以看成只有一个方法的确定名称的Protocol(协议),

  也就是说,block可以说是一个不标准的协议,,类似于我们的口头协议

  但是,就是这么一个不标准的协议,却很大的方便了我们程序的开发,试想,如果我去路边的小摊买一个冰棍,难道我和摊主还要签订一个买卖协议?不会,至少我没有见到过

  如果真要通过协议的方法来实现上述交易过程的话其实也可以,即先定义一个买卖协议,其中有一个方法,传入值为金钱,返回值为商品对象,然后摊主实现此协议,我就可以调用摊主的这个协议中的方法,最后顺利吃上我的冰棍

  你也许会想,这也太麻烦了,简直就是反人类,所以,我们可以让摊主实现一个传入值为金钱,返回值为商品对象的block对象。然后我去调用这个对象即可。

  也就是说,我们定标准的时候不用在费尽的写一个协议,在让类实现这个协议,以后只需写一个blocks方法对象,然后直接调用即可


  说了这么多,可能你对blocks还不太理解,没事,我们先从基础讲起

 

  block的基础应用:

       ①.定义并使用blocks :

        You use the ^ operator to declare a block variable and to indicate the beginning of a block literal. The body of the block itself is contained within {}

        你可以使用^操作符来定义一个block变量,^操作符出现在block的开头。block的body在{}里面。

        示例:

        int multiplier = 7;
        int (^myBlock)(int) = ^(int num) {
        return num * multiplier;
        };

        //直接使用:

         printf("%d", myBlock(3));
         // prints "21"

        注意,这种使用方法是直接将myBlock当做一个指向普通方法的指针来使用的,你可能有疑惑,我只向myBlock传了一个参数3(即是blocks中的num参数)啊,multiplier在哪呢,multiplier在myBlock的上面。下面是API文档中的解释:

       Notice that the block is able to make use of variables from the same scope in which it was defined.

      所以myBlock可以直接使用multiplier变量

      下面是上述block的示例图:

   

     ②.直接使用blocks来进行回调 :

     上面定义并使用blocks的方法就不在多说了,在自己类中定义并使用blocks)不是经常被使用,我们使用blocks一般是用作回调

     使用我之前写的Delegate中的一个例子:  IOS Protocol与Delegate详解(二)   我们把里面的"小李买茶"的例子使用blocks来解决

     这是我们需要解决的问题: 

    比如说有一个领导,旁边有他的秘书小李,有一天,领导给小李说:"小李啊,没茶叶了,你给我买点上好茶叶去,如果买回来了,我就给你涨工资如果没买上,就去站墙根去。"

   在上述问题中,小李需要有一个买茶叶的方法,但是买完茶叶回来呢?小李将茶叶买回来后的事件是领导来定义的,比如,"买回来了,我给你涨工资",这是领导制定的,小李只是执行这个方法罢了,

    所以,小李需要有三个方法,一个是成功的将茶叶买回来的事件方法,一个是没有吧茶叶买回来的事件方法,还有一个是自己买茶叶的方法,而这些事件方法,都可以直接写在买茶叶的方法中

    一般回调都是用于耗时操作,而耗时操作时候回调的方法一般都可以看做事件(Event)。例如在网上请求的耗时操作,成功后需要回调的请求成功事件(getSuccessEvent),有人讲义在Blcoks的引用的后面都加上Block,我不是很建议这样做。我建议在所有的回调事件的后面都加上Event,而不是Blcok,这样更易于自己和别人理解和维护

    下面上代码

      小李是Worker类的对象,其.m文件为;

  

#import "Worker.h"@implementation Worker/**  去买茶叶的方法  回调事件:   buySuccessEvnet : 如果购买茶叶成功后会回调的事件  buyFailEvnet    : 如果购买茶叶失败后回回调的事件  */-(void) buyTeaAndSetBuySuccessEvent:(void(^)()) buySuccessEvnet setBuyFailEvent:(void(^)()) buyFailEvnet{        NSLog(@"正在买茶叶");        //模拟买茶叶的耗时操作    [NSThread sleepForTimeInterval : 3.0 ];        NSLog(@"我买茶叶成功啦");        //如果调用者实现了此blocks的话,则回调    if (buySuccessEvnet) {                buySuccessEvnet();            }    }@end

     .h文件为:

#import <Foundation/Foundation.h>@interface Worker : NSObject{    }/**  去买茶叶的方法  回调事件:  buySuccessEvnet : 如果购买茶叶成功后会回调的事件  buyFailEvnet    : 如果购买茶叶失败后回回调的事件  */-(void) buyTeaAndSetBuySuccessEvent:(void(^)()) buySuccessEvnet setBuyFailEvent:(void(^)()) buyFailEvnet;@end


 我们就暂时不写领导的Leader类了,暂时将我们当做一个领导得意,直接在主方法中调用小李的方法

 下面是调用的方法(可以写在ViewController中的viewDidLoad方法里面):

- (void)viewDidLoad {    [super viewDidLoad];            Worker *xiaoLi = [[Worker alloc] init];        //调用小李的买茶叶的方法    [xiaoLi buyTeaAndSetBuySuccessEvent:^{                NSLog(@"小李你做的不错,这月给你涨工资");                    } setBuyFailEvent:^{                         NSLog(@"小李你成天想什么呢,去墙角站着!");            }];        }

执行后,输出的语句为:

  

正在买茶叶


三秒钟后,出现:

我买茶叶成功啦

小李你做的不错,这月给你涨工资


我们可以仔细分析一下上述代码,上述代码并没有在一个直接定义一个块变量,而是在方法中直接声明


这里有一个方面需要注意,即是如果这个方法是我的,我在这个方法里面声明了一个块,那我只需要调用即可(实际上这种调用即是回调)

而如果我调用了这个方法,这个方法需要传入块,那么我需要实现这个块,虽然这个块我没有定义变量,我只需要将这个块的body写上即可


这就是直接使用Block进行回调的例子,也是我们经常使用在项目中的Block的方法


   ③.定义并使用blocks来进行回调 :

   可能有人要问,既然我可以直接使用blocks来进行回调,那么我可以先定义一个blocks,然后在使用这个blocks进行回调吗,当然可以,但是非常不建议这样做,这样做的话有两个问题

  

   1.代码更多,不利用维护,理解

   2.可以会出现空指针的问题,即回调为空

 

   下面上这种方式写的代码:

   

   worker的.h文件:


#import <Foundation/Foundation.h>@interface Worker : NSObject{    }/**  去买茶叶的方法 */-(void) buyTea;//如果购买茶叶成功后会回调的事件//记得要使用copy属性 参考: http://blog.sina.com.cn/s/blog_6dce99b10101bsum.html@property (copy) void (^buySuccessEvnet)(void);//如果购买茶叶成功后会回调的事件@property (copy) void (^buyFailEvnet)(void);@end


   .m文件:


#import "Worker.h"@implementation Worker/**  去买茶叶的方法  */-(void) buyTea{        NSLog(@"正在买茶叶");        //模拟买茶叶的耗时操作    [NSThread sleepForTimeInterval : 3.0 ];        NSLog(@"我买茶叶成功啦");        //如果调用者实现了此blocks的话,则回调    if (self.buySuccessEvnet) {                self.buySuccessEvnet();            }            }@end

  主方法中进行调用:

   Worker *xiaoLi = [[Worker alloc] init];        //调用小李的买茶叶的方法    [xiaoLi buyTea];

   不知道你发现了没有,这时候问题就出现了,因为没有设置xiaoLi的成功和失败事件,所以直接调用根本就实现不了。这样在团队合作中是一个很致命的问题,因为别人不是很清楚你写的Worker是怎么运作的,所以调用之前没有设置其blocks,所以会出现令别人困惑的问题。下面执行一下:

   

  只会出现这些结果:


  正在买茶叶


 我买茶叶成功啦


  正确调用的代码为:


     Worker *xiaoLi = [[Worker alloc] init];        [xiaoLi setBuySuccessEvnet:^{                NSLog(@"小李你做的不错,这月给你涨工资");            }];        [xiaoLi setBuyFailEvnet:^{        NSLog(@"小李你成天想什么呢,去墙角站着!");    }];            //调用小李的买茶叶的方法    [xiaoLi buyTea];


  则结果为:

  正在买茶叶 

  我买茶叶成功啦

 小李你做的不错,这月给你涨工资


 

  所以说,不是很建议使用这种方法,代码复杂且不够直接,很容易使用者感到困惑



  










  

       


       

         

  

 

0 0
原创粉丝点击