IOS之block的使用

这篇文章主要的目的是介绍一下block如何使用,希望对大家有所帮助,同时也方便自己记忆。

block基础知识:

如果你有过类似js,lua等脚本语言的编程经验,那么你应该对闭包这个概念比较熟悉,应该会有一定的体会,我这里就不再赘述这个概念了,IOS中的block就可以实现闭包,使代码结构更巧妙灵活。
一个最简单的block如下:

^{
    // block implementation here
}

block与函数类似,只不过直接定义在一个函数里,和定义它的那个函数共享同一个范围内的变量(算是block的一大精髓吧)。

void (^blockName)() = ^{
    // block implementation here
};

从上面这个例子看出block其实就是一个值,可以把他当成int,float,或者OC对象,赋值给变量。只不过这个变量看起来有点奇怪。继续看个例子:

int (^addBlock)(int a, int b) = ^{
    return a + b;
};
int add = addBlock(2, 5);  // add == 7

这个例子中定义的block返回值是int,参数是a和b。

block有个强大之处:在声明它的范围里,所有变量都可以为其所捕获,也就是说那个范围里的全部变量,在block中依然可用,举个例子:

int c = 5;
// __block int c = 5;  // 加__block修饰,在下面的块中才可以修改c的值,否则只能访问不能修改
int (^addBlock)(int a, int b) = ^{
    // c = 3; 
    return a + b + c;  // 依然可以访问c
};
int add = addBlock(2, 5);

通过上面这些事例,相信大家对block已经有一定的感觉,有了感觉后,我这里举个闭包写法的例子,请看事例:

- (void)functionAA {

    __block a = 10;
    void (^aaa)() = ^{
        a = 5;
        // do something
    };
    void (^bbb)() = ^{
        a = 5;
        // do something
    };
    if(flag) {
        aaa();
    } else {
        bbb();
    }
}

这个例子你可能看不出闭包写法的好处,因为这个代码片段是我临时编造出来的一个小例子,没有结合在实际项目中,而实际项目block aaa和bbb中可能有很复杂的逻辑,如果不把它写在block中,而写到if中去,有种一个main函数写到底的感觉,而把这部分抽到block中不仅使得代码结构更完整,而且也体现了面向对象的封装特性,别人在阅读你这段代码的时候,只需要看你的if逻辑,根据函数名字就知道你要在这个block中做什么,不需要看你这段逻辑具体实现,待需要了解的时候再去看block中的具体实现,不看也并不影响读者理解整体代码流程,你也许会有疑问,为什么不单独在类中去加一个私有方法,把block中的内容写在新加的私有方法中,因为这样有个弊端,block所在区域内的变量不在能够访问,而需要当参数传给私有方法

如何为常用的块类型创建typedef

先看一个例子就有感觉了:

// 原来的写法
int (^JLSomeBlock)(BOOL flag, int value) = ^{
};
// 新写法
typedef int (^JLSomeBlock)(BOOL flag, int value);
JLSomeBlock block = ^(BOOL flag, int value){
};

也许从上面的例子没看出有什么不同,没体会到这种写法有什么好处,继续写几个例子。情形:比如说有个方法,需要在执行完后做一些操作,这个操作就可以放到block中去处理,可以称之为回调,如:

- (void)sendRequestWithCallBackBlock:(void(^)(NSData *data, NSError *error))callBackBlock;

// 先声明block的方式
typedef void(^JLRequestCallBackBlock)(NSData *data, NSError *error);
- (void)sendRequestWithCallBackBlock:(JLRequestCallBackBlock)callBackBlock;

当项目中有很多地方都用到了这个block,如func1:(JLRequestCallBackBlock)callBackBlock;func2:(JLRequestCallBackBlock)callBackBlock;……等等,到处都是,这个时候如果说要给这个block加一个参数,如果是采用第一种写法的话,这个时候就痛苦了,需要找到项目中所有使用了这个block的.h和.m中去逐个修改,而如果是用第二种方式,那么只需修改声明block一个地方就OK,如:

typedef void(^JLRequestCallBackBlock)(NSData *data, NSError *error, BOOL flag);

这下应该体会到了好处了吧,具体用哪种方式,做项目的时候多考虑考虑,慢慢就会体会的更深了,从而选择出最佳方案。

block和delegate做个简单比较

在IOS开发中,动态代理这种设计模式无处不在,举个例子说tableView,collectionView等各种控件,苹果都会封装好,并且通过动态代理的方式留下开口,让使用者去实现,数据源,事件捕捉等代理方法,拓展性极强。block还有个强大的用处,就是他也可以充分的体现这种思想。也经常会有程序员们会讨论用哪个好,哪个不好,其实我个人的观点还是没有哪个最好,只有哪个最合适,在不同场合不同情况。因为这里介绍的是block,所以我会举个block比delegate更好的例子:

/** 代理方式的代码片段,如果你看的不是很明白,那么请补充代理的基础知识 */
- (void)fetchData1 {
    self.fetcher1.delegate = self;
    [self.fetcher1 start];   // 会在start方法中调用代理方法
}
- (void)fetchData2 {
    self.fetcher2.delegate = self;
    [self.fetcher2 start];   // 会在start方法中调用代理方法
}
// 代理方法
- (void)dataFetcher:(JLDataFetcher *)fetcher didFinishWithData:(NSData *data){
    if(fetcher == self.fetcher1){
        // do something
        self.data = data;
    } else if(fetcher == self.fetcher2){
        // do something
        self.data = data;
    }
}

从上面代码片段可以看出,一旦情况很多种的时候,代理方法会越来越长,那么如果说采用block的方式会有什么效果呢?请看事例:

- (void)fetchData1 {
    [self.fetcher1 startWithCompletionHandler:^(NSData *data){
        // do something
        self.data = data;
    }];
}
- (void)fetchData2 {
    [self.fetcher2 startWithCompletionHandler:^(NSData *data){
        // do something
        self.data = data;
    }];
}

这里的block会在startWithCompletionHandler方法中被调用,和上面代理的方式会在start方法中调用代理方法是一个道理,这样的好处在于回调的逻辑可以各自分开处理,具体更多的好处还是自己多多体会吧。

block就简单的介绍在这了,希望大家多多给我提建议,大家共同学习进步

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值