美文网首页
OC基础 __block以及__block内存管理

OC基础 __block以及__block内存管理

作者: 我是卖报的小行家 | 来源:发表于2021-03-09 14:22 被阅读0次

    __block 修饰符的使用
    __block可以用于解决block内部无法修改auto变量值的问题
    __block不能修饰全局变量、静态变量(static)

    编译器会将__block变量包装成一个对象

    以下代码的是否编译通过,可以的话输出结果是什么

    int a = 10;
    void (^block)() = ^{
        a = 20;
        NSLog(@"a = %d",a);
    };
    

    结果:无法编译 miss__block
    源码如下

    //main函数
    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
            
            int a = 10;
            void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
        }
        return 0;
    }
    
    //block执行地址
      static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      int a = __cself->a; // bound by copy
      NSLog((NSString *)&__NSConstantStringImpl__var_folders_kh_0rp73c0s2mvfp5gjf25j5y6h0000gn_T_main_1a12fa_mi_0,a);}
    

    block执行的时候,内部是 __main_block_func_0 函数,而a的声明,是在main函数,两个函数相互独立,对于他们来说,a都是一个局部变量,而且两个函数中都对a初始化,两个函数的中a不是同一个,那怎么可以在 执行函数中,修改main函数中的局部变量呢,所以编译报错!
    如何改?

    方案一:使用static

    static int a = 10;
    void (^block)() = ^{
        a = 20;
        NSLog(@"a = %d",a);
    };
    

    因为static修饰的auto变量,最终在block中进行的不是值传递,而是地址传递,措意执行函数中的a 和 main 函数中的a,是同一个地址 ==> 等于同一个a,所以可以修改,输出20

    但是使用static,就会变成静态变量,永远在内存中
    方案二: 使用__blcok

    __block auto int a = 10;
    void (^block)() = ^{
        a = 20;
        NSLog(@"a = %d",a);
    };
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      __Block_byref_a_0 *a; // by ref ==> auto的话,是int a,__block,变成对象了
    }
    
    struct __Block_byref_a_0 {
      void *__isa;
    __Block_byref_a_0 *__forwarding;==> 指向自己的结构体
     int __flags;
     int __size;
     int a; ==> 10在这里
    };
    

    a = 20;最终转成 (a->__forwarding->a) = 20;

    解释下:__forwarding 是指向结构体本身的指针,等价于a本身,其实就是通过a的结构体指针,拿到里面的成员a,再对他赋值

    指针传递,所以可以修改 auto 变量,通过block,间接引用 auto 变量

    forwarding指针

    __block中的 _ forwarding 指针

    内存拷贝的时候,如果block从栈被copy到堆上,肯定也希望内部的变量一起存储到堆上(让变量的生命周期可控,才不会被回收)

    加入变量a在栈上,在栈上的指针,指向堆上的 block,堆上的block的 forwarding指向他自己,就可以保证,修改&获取的变量,都是堆上的变量

    最终,__block指向的变量,是指向堆上的

    forwarding指针.png

    一旦block里面要用到/访问某个对象,那就要对相应的对象进行内存管理

    当block在栈上的时候,是不会对__block变量进行强引用的
    当block在堆上的时候,会调用block内部的copy函数,而copy函数内部会调用_Block_object_assign函数,_Block_object_assign函数会根据auto变量的修饰符 ( strong、 weak、unsafe_unretained ) 做出对应的操作,看对内部auto变量进行强引用还是弱引用

    对象类型的auto变量和__block变量
    当block在栈上的时候,对他们都不会产生强引用,永远都是弱引用,
    当block被copy到堆上来的时候,都会通过copy函数来处理它们_Block_object_assign-》1.weak 2.strong

    image.png

    移除:
    当block从堆中移除的时候,会调用block内部的dispose函数,而dispose函数内部会调用_Block_objcet_dispose函数,_Block_object_dispose 类似于 release,会对auto变量进行自动释放(当引用计数器=0的时候 )
    上图


    Tips:
    在使用clang转换OC为C++代码时,可能会遇到以下问题
    cannot create __weak reference in file using manual reference

    解决方案:支持ARC、指定运行时系统版本,比如
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

    相关文章

      网友评论

          本文标题:OC基础 __block以及__block内存管理

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