快速理解Block

作者: 花间刺客 | 来源:发表于2017-03-03 09:02 被阅读47次
    block
    block的作用:保存一段代码,不是马上执行,需要调用。
    快速生成block代码:inlineBlock
    最简单的block:void(^block)()
    block是一个对象,详见官方文档搜索working with blocks
    block的几种类型及写法
    调用block

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

    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<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变量的传递

    注意点:

    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

        本文链接:https://www.haomeiwen.com/subject/wuqhgttx.html