美文网首页iOS
OC中block底层原理总结(下)

OC中block底层原理总结(下)

作者: Sweet丶 | 来源:发表于2019-04-05 11:27 被阅读14次

    关于OC中block的本质结构、block的变量捕获方式请查看OC中block底层原理总结(上)
    需要先看懂上篇文章再往下看

    一、block内部修改捕获变量的值

    不能修改的原因
    对于static局部变量和全局变量,block中是可以进行修改的;但是对于auto变量,确不能直接修改。从OC中block底层原理总结(上)里面可以知道,当引用auto变量时,捕获了一个新变量在内部了,当block调用时其内部使用的实际上是自己捕获的那个新变量,修改后两个变量值不能同步,所以修改是不合法的。

    使可修改的方法__block的工作原理
    对代码生成cpp文件查看代码本质结构:

     __block int age = 10;
     void (^block)(void) = ^{  age = 9;    };
    
    //通过命令行
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
    

    block的构成情况如下

    //block的结构体
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      __Block_byref_age_0 *age; // by ref
    };
    //  __block int age 变成了一个结构体
    struct __Block_byref_age_0 {
      void *__isa;
    __Block_byref_age_0 *__forwarding;
     int __flags;
     int __size;
     int age;
    };
    //  __block int age = 10; 这句代码初始化变成了__Block_byref_age_0结构体初始化,赋值情况为:
    {
    (void*)0, // 即 void *__isa;
    (__Block_byref_age_0 *)&age, // 即 __Block_byref_age_0 *__forwarding;
     0,  // 即 int __flags;
    sizeof(__Block_byref_age_0),  // 即 int __size;
    10 // 即 int age;
    };
    

    总结:

    1. __block声明的变量在运行时变为了一个结构体。
    2. 在block内部, 对变量修改即为对__Block_byref_age_0内部对应的成员变量修改。
    3. 在block外部,对变量进行修改也是修改的__Block_byref_age_0内部对应的那个成员变量,所以就实现了auto变量在block中可修改,值也能同步。

    当__block修饰的是对象类型的auto变量时
    生成的结构体变为

    // Person是自定义的一个类,在查看时不能用系统类所以定义
    struct __Block_byref_p_1 {
      void *__isa;
    __Block_byref_p_1 *__forwarding;
     int __flags;
     int __size;
     void (*__Block_byref_id_object_copy)(void*, void*);
     void (*__Block_byref_id_object_dispose)(void*);
     Person *__strong p;
    };
    

    相比修饰基本数据类型时的变化:

    1. 结构体中多了__Block_byref_id_object_copy__Block_byref_id_object_dispose两个函数指针
    2. 如果__block后面增加修饰符__weak时,上面的结构体中__strong会变为__weak

    查看函数指针指向函数内部得出结论:

    1. 生成的结构体最后一个成员是__weak还是__strong取决于声明时写的。
    2. 当block被拷贝时,会调用结构体__Block_byref_p_1中的copy函数,这个函数内部会决定是对对象强还是弱引用。
    3. 当block销毁时,会调用结构体__Block_byref_p_1中的dispose函数,对对象移除引用。

    二、block循环引用

    循环引用的形成即: 对象A(或强引用的对象)强引用了block, block中又强引用了对象A,导致引用计数器一直不为0,释放不了对象。
    解决的方法:正确使用@weakify 和@strongify防止block循环引用

    相关文章

      网友评论

        本文标题:OC中block底层原理总结(下)

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