iOS block从零开始
在iOS4.0之后,block横空出世,它本身封装了一段代码并将这段代码当做变量,通过block()的方式进行回调。
block的结构
先来一段简单的代码看看:
void (^myBlock)(int a) = ^(int a){
NSLog(@"%zd",a);
};
NSLog(@"旭宝爱吃鱼");
myBlock(999);
输出结果:
2016-05-03 11:27:18.571 block[5340:706252] 旭宝爱吃鱼
2016-05-03 11:27:18.571 block[5340:706252] 999
下面我们解析一下:
- void :返回的参数 void为没有返回值
- (^myBlock):myBlock 为 block 的名称
- (int a):此为参数列表
- ^(int a):传入参数
通过上面的简单介绍可以简单了解到block的结构那么下面便产生了四种格式的block。
四种block
有返回值无参:
int (^myBlock)() = ^(){
return 999;
};
有返回值有参:
int (^myBlock)(int a) = ^(int a){
NSLog(@"%zd",a);
return a;
};
无返回值无参:
void (^myBlock)() = ^(){
NSLog(@"旭宝爱吃鱼");
};
无返回值有参:
void (^myBlock)(int a) = ^(int a){
NSLog(@"%zd",a);
};
block 捕获外界局部变量
示例代码:
int a = 10;
void (^myBlock)() = ^(){
NSLog(@"旭宝爱吃鱼");
NSLog(@"%zd",a);
};
NSLog(@"旭宝爱吃鱼");
myBlock();
运行结果:
2016-05-03 11:43:32.680 block[5406:713702] 旭宝爱吃鱼
2016-05-03 11:43:32.681 block[5406:713702] 旭宝爱吃鱼
2016-05-03 11:43:32.681 block[5406:713702] 10
通过示例代码不难发现我们可以获取局部变量,那么改变局部变量是否也会改变block内的值呢。
示例代码:
int a = 10;
void (^myBlock)() = ^(){
NSLog(@"旭宝爱吃鱼");
NSLog(@"%zd",a);
};
a = 20;
NSLog(@"旭宝爱吃鱼");
myBlock();
运行结果:
2016-05-03 11:47:07.669 block[5425:715861] 旭宝爱吃鱼
2016-05-03 11:47:07.670 block[5425:715861] 旭宝爱吃鱼
2016-05-03 11:47:07.670 block[5425:715861] 10
正如结果显示,block内部的值是不会改变的,为什么呢????
示例代码:
int a = 10;
a = 20;
void (^myBlock)() = ^(){
NSLog(@"旭宝爱吃鱼");
NSLog(@"%zd",a);
};
NSLog(@"旭宝爱吃鱼");
myBlock();
运行结果:
2016-05-03 11:49:19.749 block[5450:717309] 旭宝爱吃鱼
2016-05-03 11:49:19.750 block[5450:717309] 旭宝爱吃鱼
2016-05-03 11:49:19.750 block[5450:717309] 20
情理之中的答案。
之所以block内部的值不会改变是因为block copy了局部变量。
这个问题解决后尝试在block中改变局部变量。
很不幸失败了,当我在block内改变外界的局部变量时,报错了。
可视化我想改变外界的局部变量我该怎么办呢???
改变外界的局部变量
示例代码:
__block int a = 10;
void (^myBlock)() = ^(){
NSLog(@"旭宝爱吃鱼");
NSLog(@"%zd",a);
a = 20;
};
NSLog(@"旭宝爱吃鱼");
myBlock();
NSLog(@"%zd",a);
运行结果:
2016-05-03 11:55:02.736 block[5490:721033] 旭宝爱吃鱼
2016-05-03 11:55:02.737 block[5490:721033] 旭宝爱吃鱼
2016-05-03 11:55:02.737 block[5490:721033] 10
2016-05-03 11:55:02.737 block[5490:721033] 20
我们只需要在局部变量前加__block 即可。
循环引用(我在前面的博客有所提及)
block在iOS开发中被视作是对象,因此其生命周期会一直等到持有者的生命周期结束了才会结束。另一方面,由于block捕获变量的机制,使得持有block的对象也可能被block持有,从而形成循环引用,导致两者都不能被释放:
@implementation CXObject
{
void (^_cycleReferenceBlock)(void);
}
- (void)viewDidLoad
{
[super viewDidLoad];
_cycleReferenceBlock = ^{
NSLog(@"%@", self); //引发循环引用
};
}
@end
遇到这种代码编译器只会告诉你存在警告,很多时候我们都是忽略警告的,这最后会导致内存泄露,两者都无法释放。跟普通变量存在block关键字一样的,系统提供给我们weak的关键字用来修饰对象变量,声明这是一个弱引用的对象,从而解决了循环引用的问题:
__weak typeof(*&self) weakSelf = self;
_cycleReferenceBlock = ^{
NSLog(@"%@", weakSelf); //弱指针引用,不会造成循环引用
};
使用block
在block出现之前,开发者实现回调基本都是通过代理的方式进行的。比如负责网络请求的原生类NSURLConnection类,通过多个协议方法实现请求中的事件处理。而在最新的环境下,使用的NSURLSession已经采用block的方式处理任务请求了。各种第三方网络请求框架也都在使用block进行回调处理。这种转变很大一部分原因在于block使用简单,逻辑清晰,灵活等