美文网首页
iOS Block(2)-block访问对象类型的auto变量

iOS Block(2)-block访问对象类型的auto变量

作者: switer_iOS | 来源:发表于2021-07-01 10:34 被阅读0次

    前面的block讲解中,block访问的都是基本类型的变量,接下来我们了解一下block访问对象类型的auto变量.

    image

    访问对象类型auto变量,转换后的C++代码如下:

    void(*block)(void);
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      TestClass *__strong testClass;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, TestClass *__strong _testClass, int flags=0) : testClass(_testClass) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      TestClass *__strong testClass = __cself->testClass; // bound by copy
    
                NSLog((NSString *)&__NSConstantStringImpl__var_folders__6_s9x0n6313d99yqk5pltzp6ym0000gn_T_main_c36270_mi_0,(long)((NSInteger (*)(id, SEL))(void *)objc_msgSend)((id)testClass, sel_registerName("age")));
            }
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->testClass, (void*)src->testClass, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->testClass, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static struct __main_block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
      void (*dispose)(struct __main_block_impl_0*);
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
    
            TestClass *testClass = ((TestClass *(*)(id, SEL))(void *)objc_msgSend)((id)((TestClass *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("TestClass"), sel_registerName("alloc")), sel_registerName("init"));
            ((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)testClass, sel_registerName("setAge:"), (NSInteger)20);
    
            block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, testClass, 570425344));
            ((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)testClass, sel_registerName("setAge:"), (NSInteger)25);
            ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    
        }
        return 0;
    }
    
    

    对比访问基本数据类型:
    相同点:block结构体内部也存在一个与被访问的变量同名的成员变量,本demo中,也就是TestClass *__strong testClass;即testClass实例。
    不同点:__main_block_desc_0结构体的实现,发现其内部增加了两个函数:
    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    void (*dispose)(struct __main_block_impl_0*);
    补充:关于copy函数与dispose函数的调用机制

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

    如果block从堆上移除,会调用block内部的dispose函数。dispose函数内部会调用_Block_object_dispose函数,_Block_object_dispose函数会自动释放引用的 auto变量(release)。

    所谓的弱引用就是在变量前面加上“__weak”修饰符,默认是"__strong"修饰的,即默认对变量为强引用。创建testClass实例加上“__weak”前缀,__weak __weak TestClass *weakTestClass = testClass;对应C++代码中,block结构体内部的成员testClass就声明成 TestClass *__weak weakTestClass;
    在使用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
    MRC下没有"强引用"的概念,只有retain、release概念。栈空间block不会保住引用对象的命,即不会对引用对象进行强引用。但是堆空间block通过强引用被访问对象,从而延长被引用对象的生命周期。
    所以可以对栈内存的block进行copy操作,变成堆内存上的block,这样被block引用的对象生命周期就会保住,等到block销毁的时候,被引用的对象才会销毁。
    不管ARC、MRC,栈空间block都不会持有对象,如果是堆空间block,有能力保住被引用对象的命,换成ARC下的说法就是“强引用”,MRC下没有“强引用”。
    在ARC下,我们通过demoA、demoB看看以往开发中,在堆上block内访问oc对象使用不同修饰符造成的结果:

    demoA:
    image
    demoB:
    image

    demoB中,testClass对象默认被__strong修饰符修饰,block会对其强引用,转换为C++环境后block结构体内容如下:

    struct __main_block_impl_0 {
     struct __block_impl impl;
     struct __main_block_desc_0* Desc;
     TestClass *__strong testClass;
     __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, TestClass *__strong _testClass, int flags=0) : testClass(_testClass) {
       impl.isa = &_NSConcreteStackBlock;
       impl.Flags = flags;
       impl.FuncPtr = fp;
       Desc = desc;
     }
    };
    
    

    block结构体内成员变量testClass就是就是OC中alloc出来的testClass对象,由于该block在堆上,在当前情况下,只要该block没有被销毁,block的内存就一直存在,其内部成员变量testClass由于被"__strong"强引用修饰,也会一直存在,故而TestClass类也一直存在于内存中,这在很多情况下会成为App内存泄露的根源。

    总结:

    当block内部访问了对象类型的auto变量时

    • 如果block是在栈上,将不会对auto变量产生强引用
    • 如果block是在堆上(copy到栈上时__main_block_desc_0增加了copy和dipose函数)
      1. 会调用block内部的copy函数
      2. copy函数内部会调用_Block_object_assign函数
      3. _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(对atuo变量)
    • 如果block从堆上移除
      1. 会调用block内部的dispose函数
      2. dispose函数内部会调用_Block_object_dispose函数
      3. _Block_object_dispose函数会自动释放引用的auto变量(release)
    image

    相关文章

      网友评论

          本文标题:iOS Block(2)-block访问对象类型的auto变量

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