美文网首页iOS
《OC高级编程》之 Blocks(二)

《OC高级编程》之 Blocks(二)

作者: 毛线sama | 来源:发表于2019-03-08 21:29 被阅读9次

    Block 的实现

     

    Block 的实质

        Block 实质上是 Object-C 对象

        所谓截获自动变量值,意味着在执行 Block 语法时,Block 表达式所使用的自动变量值被保存在 Block 的结构体实例中

    (有一、、复杂,随缘看吧)

        Object-C 中由类生成对象,意味着,像结构体这样“生成由该类生成的对象的结构体实例”,生成的各个对象,即由该类生成的对象的各个结构体实例,通过成员变量 isa 保持该类的结构体实例指针
     

    截获自动变量

        Block 语法表达式中使用的自动变量被作为成员变量被追加到结构体中(自动变量的截取只针对 Block 中使用的自动变量)

        所谓“截取自动变量”意味着在执行 Block 语法时,Block 语法表达式所使用的自动变量值被保存到 Block 的结构体实例(即 Block 自身中),但无法实现改写截取变量的值
     

    __block 说明符

        想在 Block 中保存值,有两种方法:

    1. 使用静态变量/静态全局变量/全局变量 对静态全局变量和全局变量的访问与转换前完全相同,但静态变量则是通过指针对其进行访问,将其指针传给结构体并保存
    2. 使用 __ block,__ block 变量实际是结构体类型的自动变量,持有指向该实例自身的 __ forwarding 指针,通过 __forwarding 指针来访问成员变量(即原自动变量)
       

    Block 存储域

        Block 和 __block 变量实质上都是在栈上的结构体实例,Block 的类别有 _NSConcreteStackBlock,_NSConcreteGlobalBlock,_NSConcreteMallocBlock

        三者分别储存在栈,数据区域(.data区),堆中,目前出现的都是在栈上的,但在记述全局变量的地方使用 Block 时,生成的是 _NSConcreteGlobalBlock 类对象,当 Block 语法表达式中不使用应截获的自动变量时,也会被设置在数据区域

        Block 超出变量作用域可存在的原因:Blocks 提供了将 Block 和 __ block 变量从栈上复制到堆上的方法来解决这个问题,变量作用域结束时,栈上的 Block 和 __ block 被废弃,但堆上的不受影响,而 __ forwarding 指针则指向堆上的结构体实例,使得能够正确访问 __block 变量

        将 Block 作为函数返回值返回时,会自动生成复制到堆上的代码,除此之外,需要使用 copy 方法

        编译器不能进行判断的情况:

    • 向方法或函数的参数中传递 Block 时

      但如果在方法/函数中适当复制了传递来的参数,就不需在调用之前手动添加了,以下方法/函数不需要手动复制:

    • Cocoa 框架的方法且方法名中含有 usingBlock 等时

    • Grand Central Dispatch 的 API

    Block的类 副本源的配置储存域 复制效果
    _NSConcreteStackBlock 复制到堆
    _NSConcreteGlobalBlock 数据区域 不做
    _NSConcreteMallocBlock 引用计数增加

    ARC 有效时多次调用 copy 方法也没有问题

     

    __block 变量存储域

    __block 变量的配置存储域 Block 从栈复制到堆时的影响
    复制到堆并被Block持有
    被Block持有

        在栈上的 __ block 变量用结构体实例在 __ block 变量从栈复制到堆上时,会将 __ forwarding 的值替换为复制目标堆上的 __ block 变量用结构体的位置,以达到顺利访问同一个 __block 变量的目的

    __block int val = 0;
    void (^blk)(void) = [^{++val} copy]; //使用初始化好的 __block 变量
    ++val;  //使用与Block无关的变量
    blk();
    NSLog(@"%d", val);
    //均可转换为:
    //++(val.__forwarding->val);
    

     

    截获对象

        被截取的对象是 Block 用的结构体中附有 __strong 修饰符的成员变量,并通过copy 和 dispose 函数来使 Block 用结构体持有或释放该对象,在 Block 从栈复制到堆时及堆上的 Block 被废弃时会调用这些函数。会复制到堆的情况:

    • 调用 copy
    • 作为函数返回值
    • 将 Block 赋值给附有 __strong 修饰符的 id 或 block 类型时(编译器自动调用 copy)
    • 在方法名中含有 usingBlock 的 Cocoa 框架方法或 Grand Central Dispatch 的 API 中传递 Block 时

        因此,Block 中使用的赋值给附有 __ strong 修饰符的自动变量的对象和赋值到堆上的 __ block 变量由于被堆上的 Block 持有,因而可超出其变量作用域而存在。(只有调用了 copy 函数才能持有捕获的附有 __strong 修饰符的对象类型的自动变量)

        __ block 变量为附有 __ strong 修饰符的 id 类型的自动变量时,和在 Block 中使用赋值给 __strong 修饰符的对象自动变量的对象相同,(通过相似的函数持有和释放)

        如果在 Block 中使用附有 __ weak 修饰符的 id 变量,可正常执行,作用域结束时 nil 被赋值给该变量。若同时指定 __ block 和 __ weak,执行结果一样
     

    Block 循环引用

        类对象的 Block 类型成员变量中使用 self 或其他成员变量时会形成循环引用

    1. 可声明 __weak 修饰符的变量,并将 self 赋值使用

    2. 使用 __ block 变量来避免,并在 Block 语法表达式中将 __block 变量赋值 nil

      但如果不执行该 Block,就会循环引用并引起内存泄漏

      优:通过 __block 可控制对象的持有期间

      缺:必须执行 Block

    相关文章

      网友评论

        本文标题:《OC高级编程》之 Blocks(二)

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