快速理解Block

快速理解Block_第1张图片
block

block的作用:保存一段代码,不是马上执行,需要调用。
快速生成block代码: inlineBlock
最简单的block:void(^block)()
block是一个对象,详见官方文档搜索working with blocks
快速理解Block_第2张图片
block的几种类型及写法

调用block

调用block方式block名称(参数)

快速理解Block_第3张图片
e.g.

注意点:1、声明block属性时,不同于其他数据类型+变量名形式,直接写block类型,但用typedef定义一个block类型的别名也可以实现常见的的数据类型+变量名形式,如上图黄色框所示。2、block必须用strong

使用场景:传值

从下一个控制器传到上一个控制器可以代理,在传值的控制器中设置代理,在需要传值的控制器中遵守代理协议方法;逆传时,也可以利用block可以携带参数的特性替代代理方法传值。
简单举个例子创建A、B两个控制器,A present 到B,在B dismiss 时传值,在此之前先在B声明一个带参数的block属性,在A中定义这个block,再在A中调用这个block。

总结
  1. 声明一个带参数的block属性
  2. 在需要传值的block中定义block
  3. 在传值控制器中调用block

传值注意点:在传值时,为了安全起见,要先判断是否定义了block,或是是否响应了代理方法。

Block的内存管理

MRC:

  1. 如果block访问外部局部变量,block存放在栈中;
  2. 在MRC中不能使用retain声明block,依然放在栈里面,会自动销毁;
  3. 使用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循环引用

注意点:

  1. block会对外部所有强指针对象强引用
  2. block不会对外部弱指针对象给强引用
    (e.g.)
    @interface ModalViewController () @property (nonatomic, strong) void(^block)(); @end

block = ^{ NSLog(@"%@",self); }
(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self dismissViewControllerAnimated:YES completion:nil]; }

- (void)dealloc { NSLog(@"%s",__func__); }
ModalViewController dismiss后dealloc并未打印,表明控制器并未销毁,问题出在block块中NSLog(@"%@",self);self强引用了ModalViewController造成循环引用,如下图:

快速理解Block_第4张图片

解决办法:声明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
快速理解Block_第5张图片

内存分析:afterBlock代码块内引用weakSelf,在dismiss后由于没有强指针指向_block,_block销毁,所以在afterBlock外部用强指针,指向_block;完整内存分析图如下:
快速理解Block_第6张图片

block变量的传递

注意点:

  1. 如果block访问外部局部变量没有被任何关键字修饰,都是值传递
  2. block访问外部变量被__block,static修饰,指针传递
  3. 访问的全部变量,也是指针传递
    __block int a = 0;
    void(^block)() = ^{
    NSLog(@"%d",a);
    }; a = 5; block();输出结果为5
    p.s.__block修饰的变量在block内部使用时,block不会只对变量作只读拷贝操作,变量会存储到block结构中去
block开发使用场景(参数使用)

什么时候使用block充当参数传递?思想:封装一个功能,这个功能做什么事情有外部决定,但是什么时候调用由内部决定,这个时候就需要把block充当参数去使用

你可能感兴趣的:(快速理解Block)