Blocks是C语言的扩充功能。可以概括为:带有自动变量(局部变量)的匿名函数。
所谓的匿名函数,就是没有名字的函数,在C语言中是不允许的。
在计算机科学中,这种概念也称为:“闭包Closure”、“lambda计算”等。
而且一般而言,函数或者方法里是不能又出现一个函数的,但是Blocks作为一种数据类型,是可以的。通常Blocks用于封装代码。因为即便是写在函数体内部,不去调用它,就不会执行。
//无参数 无返回值
void (^block1)(void) = ^void(void){
NSLog(@"无参数 无返回值");
};
//无参数 无返回值 可省略表达
void (^block2)(void) = ^{
NSLog(@"无参数 无返回值");
};
//有参数 无返回值
void (^block3)(NSString* name) = ^(NSString* name){
NSLog(@"有参数 无返回值");
};
//有参数 无返回值 前面声明的参数名称可参略
void (^block4)(NSString*) = ^(NSString* name){
NSLog(@"有参数 无返回值");
};
//有参数 有返回值
int (^block5)(NSString* name) = ^ int (NSString* name) {
NSLog(@"有参数 有返回值");
return 1;
};
int number = block5(@"haha");
NSLog(@"howMuch : %d",number);
运行结果:
text2021[50774:5632858] 有参数 有返回值
text2021[50774:5632858] howMuch : 1
当Blocks作为参数时的书写格式比较怪异,要适应一下:
普通函数与Blocks函数对比:
- (void)block2:(NSString*)name{
NSLog(@"name: %@",name);
}
- (void)block3:(void(^)())callBackBlock{
NSLog(@"调用了block3");
callBackBlock();
}
我们可以注意到在Block3的方法里面,调用了callBackBlock()方法。
我们看看如何调用:
- (void)block4{
NSLog(@"执行block4");
[self block3:^{
NSLog(@"我又回到block4");
}];
}
- (void)block3:(void(^)())callBackBlock{
NSLog(@"调用了block3");
callBackBlock();
}
运行结果:
text2021[51007:5682882] 执行block4
text2021[51007:5682882] 调用了block3
text2021[51007:5682882] 我又回到block4
由此可见,Blocks的作用就是改变原本代码的执行,如上述的结果,首先调用的是block4的方法,然后返回到block3处理了一下,再回到原来的地方接着处理block4.
它使得代码更加灵活。
当有参数有返回值的blocks作为函数的参数的时候,写法更是复杂难记,但在开发中是十分常见的,因此只要熟悉、适应了就好。
- (void)block6{
NSLog(@"执行block6");
int spend = 5000; //花费5000元
//计算人均需要AA多少钱
[self calculateInfo:^(int count, CGFloat discount) {
NSLog(@"我又回到block6");
CGFloat aa = spend / count * discount;
NSLog(@"人均花费:%.2f",aa);
}];
}
- (void)calculateInfo:(void (^)(int count,CGFloat discount))callBackBlock{
NSLog(@"执行calculateInfo");
int count = 50; //人数为50人
CGFloat discount = 0.8; //折扣为8折
callBackBlock(count,discount);
}
运行结果:
text2021[52299:5985655] 执行block6
text2021[52299:5985655] 执行calculateInfo
text2021[52299:5985655] 我又回到block6
text2021[52299:5985655] 人均花费:80.00
假设:有一个人类,每天的事情大部分都是千篇一律的,无非就是睡觉、吃饭、工作、学习、谈恋爱等等,虽然大部分相同,但也总是有些差异的。
如果将该人类的每一天要做的事情写成代码,大致上就是:
第一天:起床 吃饭 工作 吃饭 睡觉
第二天:起床 吃饭 谈恋爱 吃饭 睡觉
第三天:起床 吃饭 学习 吃饭 睡觉
那么我们可以发现三天里只有中间部分环节是不同的,显然如果这样编写代码,是效率非常低的,而Blocks就能解决这样的问题。
代码:
- (void)block7{
[self date:@"day1" Daily:^{
NSLog(@"工作");
}];
[self date:@"day2" Daily:^{
NSLog(@"谈恋爱");
}];
[self date:@"day3" Daily:^{
NSLog(@"学习");
}];
}
- (void)date:(NSString*)date Daily:(void (^)(void))somethingDifferent{
NSLog(@"%@",date);
NSLog(@"起床");
NSLog(@"吃饭");
somethingDifferent();
NSLog(@"吃饭");
NSLog(@"睡觉");
}
运行结果:
text2021[55174:6433382] day1
text2021[55174:6433382] 起床
text2021[55174:6433382] 吃饭
text2021[55174:6433382] 工作
text2021[55174:6433382] 吃饭
text2021[55174:6433382] 睡觉
text2021[55174:6433382] day2
text2021[55174:6433382] 起床
text2021[55174:6433382] 吃饭
text2021[55174:6433382] 谈恋爱
text2021[55174:6433382] 吃饭
text2021[55174:6433382] 睡觉
text2021[55174:6433382] day3
text2021[55174:6433382] 起床
text2021[55174:6433382] 吃饭
text2021[55174:6433382] 学习
text2021[55174:6433382] 吃饭
text2021[55174:6433382] 睡觉
在Blocks中我们可以使用局部变量,是因为从Blocks外部传递进来的局部变量,Block会视为是一个常量(不可改变的量),只读不可写。
如果要在Blocks内修改该局部变量,需要为该变量做一个__block 的声明,这样的话,编译器才会吧该变量的地址传进去block中。
- (void)block8{
int number = 10;
void (^test)(void) = ^{
//只是声明Block,不会被调用
NSLog(@"%d",number);//可读
number = 20;
};
test(); //调用Blocks
}
应改为:
__block int number = 10;
void (^test)(void) = ^{
NSLog(@"%d",number);//可读
number = 20;
};
test();
另注意:在block内部使用对象的时候,会对对象有一个强引用,知悉即可。