这篇文章主要的目的是介绍一下block如何使用,希望对大家有所帮助,同时也方便自己记忆。
block基础知识
1. 下面通过实例由浅入深逐步分析:
- 例1:
一个最简单的block如下:
^{
// block implementation here
}
- 例2:
从下面这个例子看出block其实就是一个值,可以把他当成int,float,或者OC对象,赋值给变量。只不过这个变量看起来有点奇怪。
void (^blockName)() = ^{ // block implementation here};
- 例3:
这个例子中定义的block返回值是int,参数是a和b。
int (^addBlock)(int a, int b) = ^{
return a + b;
};
int add = addBlock(2, 5); // add == 7
- 例4:
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);
2. block闭包写法
如果你有过类似js、lua等脚本语言的编程经验,那么你应该对闭包这个概念比较熟悉,IOS中的block就可以实现闭包,使代码结构更巧妙灵活。
- (void)functionAA {
int a = 10;
if (flag) {
NSLog(@"do some thing1 with a %@", @(a));
NSLog(@"do some thing2 with a %@", @(a));
} else {
if (flag1) {
NSLog(@"do some thing1 with a %@", @(a));
NSLog(@"do some thing2 with a %@", @(a));
} else {
NSLog(@"do some thing3");
}
}
}
通过上面这段代码不难看出flag和flag1中要做的事其实是一样的,然而有什么办法能让他们复用起来,使代码结构更简洁,相信很多人想到可单独抽一个私有方法出来,这的确是一个办法,不过这样有个麻烦的事,需要把参数传递到私有方法,下面对这段方法进行改造:
- (void)functionAA {
int a = 10;
void (^doSomeThingBlock)() = ^{
NSLog(@"do some thing1 with a %@", @(a));
NSLog(@"do some thing2 with a %@", @(a));
};
if (flag) {
doSomeThingBlock();
} else {
if (flag1) {
doSomeThingBlock();
} else {
NSLog(@"do some thing3");
}
}
}
对比这两段代码,这里的doSomeThingBlock就是闭包的写法,而且doSomeThingBlock可以访问到变量a,如果使用抽出私有方法的方式就需要把变量a传给私有方法。
3. 如何为常用的块类型创建typedef
先看一个例子:
// 原来的写法
int (^JLSomeBlock)(BOOL flag, int value) = ^{};
// 新写法
typedef int (^JLSomeBlock)(BOOL flag, int value);
JLSomeBlock block = ^(BOOL flag, int value){};
对于一些常用的block,我们可以用typedef定义,下面再举个例子进行分析:
- (void)doFuncWithCallBackBlock:(void (^)(int a, int b))block;
// 采用声明block的方式
typedef void(^callBackBlock)(int a, int b);
- (void)doFuncWithCallBackBlock:(callBackBlock)block;
设想一下,当有特殊要求要给doFuncWithCallBackBlock方法的block参数加一个参数NSData,如果采用的是第一种方式,这个时候就痛苦了,需要在项目中全局去查收所有doFuncWithCallBackBlock,然后进行逐个修改,那么如果用的是第二种方式,只需要将声明部分修改成typedef void(^callBackBlock)(int a, int b, NSData *data);
就搞定了。具体用哪种方式,做项目的时候多考虑考虑,慢慢就会体会的更深了,从而选择出最佳方案。
4. block和delegate做个简单比较
在IOS开发中,动态代理设计模式无处不在,如:tableView、collectionView等各种控件,苹果都会封装好,并且通过delegate的方式留下开口,让使用者去实现它的delegate中定义的方法,苹果会在它封装的底层代码中去调用我们实现的方法,拓展性极强。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的方式会有什么效果呢?请看事例:
- 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方法中调用代理方法是一个道理,这样的好处在于回调的逻辑可以各自分开处理,具体更多的好处还是自己多多体会吧。
网友评论