例行科普
Block 是 iOS4 之后添加的一种语法结构,也成为闭包,或者匿名函数,在iOS开发过程中应用较多,使用也比较方便,常见于传值、回调等。
1.几种类型的Block
Block的定义方式:
返回类型(^block变量名)(参数列表) = ^(形参列表){};
系统提供了一个定义block的宏,输入inlineBlock就会出现
<#returnType#>(^<#blockName#>)(<#parameterTypes#>) = ^(<#parameters#>) {
<#statements#>
};
1.1 无参数无返回值的block
void(^blockName)();//声明
blockName = ^(){
NSLog(@"调用");
};//实现
blockName();//调用
//当然也可以连一起写,看个人喜好
//void(^blockName)() = ^(){
// NSLog(@"调用");
//};
1.2 无参数有返回值的block
int(^getNum)() = ^(){
return 1;
};
NSLog(@"%d", getNum());
1.3 有参数无返回值的block
void(^blockName)(int) = ^(int a){
NSLog(@"%d", a);
};
blockName(3);
1.4 有参数有返回值的block
int(^addNum)(int, int) = ^(int a, int b){
return a + b;
};
NSLog(@"%d", addNum(242, 424));
1.5 Block的重命名
block的写法还是比较麻烦的,所以系统提供了一个重命名的功能
typedef void(^blockType)(int, int);
这样写的话,一个有两个参数,无返回值的block类型就变成了blockType,然后只需要用这个类型声明block就可以了。
@property (strong, nonatomic) blockType aBlock;
2 上面几种只是block的初级用法,下面来点中级的。
2.1 Block做为参数
Block作为参数大家应该不陌生,因为最常用的三方库AFN就使用了这种方式。这里就不贴AFN的源码了,直接仿写一个例子,当然,具体内容并没有实现,只是写了一个思路。
- (void)requestWith:(NSString *)url
with:(void(^)(id dataSource))success
with:(void(^)(NSError *error))failure
{
if (success) {
success(@"你获取的数据");
}
if (failure) {
failure([NSError new]);// 报错信息
}
}
//调用
[self requestWith:@"url" with:^(id dataSource) {
//请求成功
} with:^(NSError *error) {
//请求失败
}];
2.2 Block作为返回值
Block作为返回值看起来就比较奇怪了,最常见的就是masomry了,对就是那个点来点去的,看起来高大上的语法,也就是链式编程。
先在ViewController里声明了一个属性,来存放数据
@property (nonatomic, assign)int result;
然后写返回block的方法:
- (ViewController *(^)(int))addNum
{
return ^(int number){
_result += number;
return self;
};
}
调用:
self.addNum(1).addNum(2).addNum(3);
NSLog(@"%d", _result);
解释起来应该就是这样的:
self.addNum相当于get方法的调用:[self addNum];
self.addNum返回的是一个block,所以你可以给他一个参数1,于是写成这样:self.addNum(1)
然后block返回的又是self,所以继续调用addNum
如此循环下去.......
Viewcontroller可以换成任何一个类,实现方式是一样的
2.3 Block对外部变量的修改
int a = 3;
void(^block)() = ^{
NSLog(@"%d",a);
};
a = 5;
block();
你可以看到,打印出来的a的值是5,也就是说,block获取的是a的值,也就是3,所以当a变成5的时候,block内部打印的值并没有改变。
static int a = 3;
void(^block)() = ^{
NSLog(@"%d",a);
};
a = 5;
block();
这个应该就不用解释了,和上面是相反的结果,block内部打印的值会随着a的值改变而改变,也就是说block获取的是a的指针。
总结起来就是:
(1)如果是局部变量,Block获取的是值。
(2)如果是静态变量,全局变量, __block修饰的变量,block获取的是指针。
说到这就得说一下这个 __block修饰符了,正常情况下block内部是不允许修改局部变量的值的,但是可以修改静态变量和全局变量,为什么?上面提到了,一种是获取值,一种是获取指针,获取值的当然就不让改了,所以需要在局部变量前加个修饰符 __block:
__block int b = 7;
void(^block)() = ^{
b = 20;
};
这样block获取的就是指针了,也就可以修改了。
3 Block传值
估计这是提到block第一个就想到的功能了,block可以实现页面间的传值功能,直接上例子。
先在第二个页面的.h文件里声明一个block
typedef void(^block)(NSString);
@interface ViewControllerTwo : UIViewController
@property (strong, nonatomic) block aBlock;
@end
然后写下回调方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.aBlock) {
self.aBlock(@"回调传值");
}
[self dismissViewControllerAnimated:YES completion:nil];
}
然后回到第一个页面引入头文件,写跳转方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
ViewControllerTwo *VC = [ViewControllerTwo new];
VC.aBlock = ^(NSString *str){
NSLog(@"%@", str);
};
[self presentViewController: VC animated:YES completion:nil];
}
这样block传值就完成了。
4 Block的循环引用
在block内部使用self之前,写上这样一句代码
__weak typeof(self) weakSelf = self;
然后将block使用的self全部替换成weakSelf就可以避免循环引用了。
结语:限于水平,本文只写了一些基本用法和注意事项,如果文中存在错误请指出,我会及时修改。