美文网首页iOS专攻
你想了解的Block,都告诉你

你想了解的Block,都告诉你

作者: han_zero | 来源:发表于2019-08-28 14:01 被阅读0次

    Block

    block的本质 -- -- OC对象

    block最终都是继承自NSBlock类型,而NSBlock继承于NSObjcet。那么block其中的isa指针其实是来自NSObject中的。这也更加印证了block的本质其实就是OC对象

    block的捕获机制

    局部变量:
    会被block捕获到内部
    auto类型的是值传递,内部不能修改值
    static是指针传递,可以修改值。

    全局变量:
    不会被捕获到block内部
    可以修改值

    Q:为什么局部变量需要捕获?
    考虑作用域的问题,需要跨函数访问,就需要捕获
    <style>
    td
    {
    text-align:center;
    }
    </style>

    <table border="1" bgcolor="#778899" class="td">
    <tr>
    <th colspan="2">变量类型</th>
    <th>捕获到block内部</th>
    <th>访问方式</th>
    </tr>
    <tr>
    <td rowspan="2">局部变量</td>
    <td>auto</td>
    <td>√️</td>
    <td>值传递</td>
    </tr>
    <tr>
    <td>static</td>
    <td>√</td>
    <td>地址传递</td>
    </tr>
    <tr>
    <td colspan="2">全局变量</td>
    <td>×</td>
    <td>直接访问</td>
    </tr>
    </table>

    (html代码转义失败了,直接看截图吧😅)


    image.png

    block有哪几种类型

    • _NSConcreteStackBlock:
      只用到外部局部变量、成员属性变量,且没有强指针引用的block都是StackBlock。
      StackBlock的生命周期由系统控制的,一旦返回之后,就被系统销毁了。
    • _NSConcreteMallocBlock:
      有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,没有强指针引用即销毁,生命周期由程序员控制
    • _NSConcreteGlobalBlock:
      没有用到外界变量或只用到全局变量、静态变量的block为_NSConcreteGlobalBlock,生命周期从创建到应用程序结束

    ARC环境下,一旦Block赋值就会触发copy,__block就会copy到堆上,Block也是__NSMallocBlock。ARC环境下也是存在__NSStackBlock的时候,这种情况下,__block就在栈上。
    MRC环境下,只有copy,__block才会被复制到堆上,否则,__block一直都在栈上,block也只是__NSStackBlock,这个时候__forwarding指针就只指向自己了。

    在MRC环境下,__block根本不会对指针所指向的对象执行copy操作,而只是把指针进行的复制。
    而在ARC环境下,对于声明为__block的外部对象,在block内部会进行retain,以至于在block环境内能安全的引用外部对象,所以才会产生循环引用的问题!

    <pre>
    int age = 1;
    void (^block1)(void) = ^{
    NSLog(@"block1");
    };

    void (^block2)(void) = ^{
    NSLog(@"block2:%d",age);
    };

    NSLog(@"%@/%@/%@",[block1 class],[block2 class],[^{
    NSLog(@"block3:%d",age);
    } class]);
    </pre>

    堆block

    1.如果block被拷贝到堆上:

    • 会调用block内部的copy函数
    • copy函数内部会调用_Block_object_assign函数
    • _Block_object_assign函数会根据auto变量的修饰符 (__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用

    2.如果block从堆上移除

    • 会调用block内部的dispose函数
    • dispose函数内部会调用_Block_object_dispose函数
    • _Block_object_dispose函数会自动释放引用的auto变量(release)

    Q:当block内部访问了对象类型的auto变量时,是否会强引用?

    • 如果block在栈空间,不管外部变量是强引用还是弱引用,block都会弱引用访问对象
    • 如果block在堆空间,如果外部强引用,block内部也是强引用;如果外部弱引用,block内部也是弱引用

    解决堆block的循环引用办法:

    • 释放block
    • 在block里不要强引用对象

    masonry解决的办法是释放block

    - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
        self.translatesAutoresizingMaskIntoConstraints = NO;
        MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
        block(constraintMaker);
        return [constraintMaker install];
    }
    

    看as_makeConstraints的方法实现会发现设置布局的方法中的block对象并没有被View所引用,block(constraintMaker)而是直接在方法内部同步执行,执行完以后block将释放,其中捕捉的外部变量的引用计数也将还原到之前。这个block只是个栈block,栈block有个特性就是它执行完毕之后就出栈,出栈了就会被释放掉,构不成循环引用。

    __weak typeof(person) weakPerson = person;
    __unsafe_unretained Person *person = [[Person alloc] init];
    __block Person *person = [[Person alloc] init];
    

    __weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil
    __unsafe_unretained:不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变
    __block:必须把引用对象置为nil,并且要调用该block

    weakSelf 是为了block不持有self,避免Retain Circle循环引用。在 Block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。strongSelf的目的是因为一旦进入block执行,假设不允许self在这个执行过程中释放,就需要加入strongSelf。block执行完后这个strongSelf 会自动释放,没有不会存在循环引用问题。如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。
    strongSelf实质是一个局部变量(在block这个“函数”里面的局部变量),当block执行完毕就会释放自动变量strongSelf,不会对self进行一直进行强引用。

    相关文章

      网友评论

        本文标题:你想了解的Block,都告诉你

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