block的作用:保存一段代码,不是马上执行,需要调用。
快速生成block代码:
inlineBlock
最简单的block:void(^block)()
block是一个对象,详见官方文档搜索working with blocks
block的几种类型及写法
调用block
调用block方式block名称(参数)
注意点:1、声明block属性时,不同于其他数据类型+变量名形式,直接写block类型,但用typedef定义一个block类型的别名也可以实现常见的的数据类型+变量名形式,如上图黄色框所示。2、block必须用strong
使用场景:传值
从下一个控制器传到上一个控制器可以代理,在传值的控制器中设置代理,在需要传值的控制器中遵守代理协议方法;逆传时,也可以利用block可以携带参数的特性替代代理方法传值。
简单举个例子创建A、B两个控制器,A present 到B,在B dismiss 时传值,在此之前先在B声明一个带参数的block属性,在A中定义这个block,再在A中调用这个block。
总结
- 声明一个带参数的block属性
- 在需要传值的block中定义block
- 在传值控制器中调用block
传值注意点:在传值时,为了安全起见,要先判断是否定义了block,或是是否响应了代理方法。
Block的内存管理
MRC:
- 如果block访问外部局部变量,block存放在栈中;
- 在MRC中不能使用retain声明block,依然放在栈里面,会自动销毁;
- 使用copy声明block,才会放在堆里面
ARC
如果block访问外部的局部变量,block存放在"堆"里面
ARC:使用strong声明block,不要使用weak
(p.s.) ARC管理原则:默认一个局部变量对象,都是强指针,存放堆里面
ARC:如果一个对象没有强指针引用,就会销毁
block管理原则
如果block没有访问外部的局部变量或者局部变量被static修饰,block默认存放在"全局区"
(e.g.)
int a = 2; void(^block)() = ^{ NSLog(@"%d",a); }; _block = block; a = 4; NSLog(@"%@",block);
打印结果为2;在变量a前加入static打印结果为4;
block循环引用
注意点:
- block会对外部所有强指针对象强引用
- block不会对外部弱指针对象给强引用
(e.g.)
@interface ModalViewController () @property (nonatomic, strong) void(^block)(); @end
block = ^{ NSLog(@"%@",self); }
(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self dismissViewControllerAnimated:YES completion:nil]; }
- (void)dealloc { NSLog(@"%s",__func__); }
ModalViewController dismiss后dealloc并未打印,表明控制器并未销毁,问题出在block块中NSLog(@"%@",self);
self强引用了ModalViewController造成循环引用,如下图:
解决办法:声明weakSelf弱指针
__weak typeof(self) weakSelf = self;
(block不会对外部弱指针对象给强引用)(e.g.)
如果在block代码块中加入延迟或者异步操作时
__weak typeof(self) weakSelf = self; _block = ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@", weakSelf); }); }; _block();
代码块中weakSelf打印为null解决办法:在afterBlock外部添加strongSelf
内存分析:afterBlock代码块内引用weakSelf,在dismiss后由于没有强指针指向_block,_block销毁,所以在afterBlock外部用强指针,指向_block;完整内存分析图如下:
block变量的传递
注意点:
- 如果block访问外部局部变量没有被任何关键字修饰,都是值传递
- block访问外部变量被__block,static修饰,指针传递
- 访问的全部变量,也是指针传递
__block int a = 0;
void(^block)() = ^{
NSLog(@"%d",a);
}; a = 5; block();
输出结果为5
p.s.__block修饰的变量在block内部使用时,block不会只对变量作只读拷贝操作,变量会存储到block结构中去
block开发使用场景(参数使用)
什么时候使用block充当参数传递?思想:封装一个功能,这个功能做什么事情有外部决定,但是什么时候调用由内部决定,这个时候就需要把block充当参数去使用
网友评论