美文网首页
ios block的完全理解循环引用

ios block的完全理解循环引用

作者: wangyongyue | 来源:发表于2018-10-15 18:11 被阅读8次

    ios 魔法block,是我最喜欢的没有之一,好用又好玩。
    对于它的实现方式一直是模棱两可,从来不敢说完全懂了。
    这两天不知道怎么回事,脑袋里冒出来一些想法,想把block再深究一下,
    看看他到底有什么不一样?
    如果有什么不对的对方还望提醒,在此谢过。

    1.先看看block的声明会发生什么

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    typedef void(^NameBlock)(NSString *name);
    @interface TestBlock : NSObject
    @property(nonatomic,copy)NameBlock block;
    - (void)run:(NameBlock)block;
    @end
    
    NS_ASSUME_NONNULL_END
    #import "TestBlock.h"
    
    @implementation TestBlock
    
    - (void)run:(NameBlock)block{
        
        NSLog(@"nameBlock");
    }
    @end
    

    上面的是OC的代码,很简单声明了一个block,然后run方法传参是block;

    //__OFFSETOFIVAR__  字面理解 TYPE的变量的地址偏移量,用来寻找变量属性的。
    //#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)
    //NSConstantStringImpl__var_folders_pr_mqg77fcj5hd0vp7mmx1lnf6h0000g//n_T_TestBlock_555780_mi_0 __attribute__ 看着灰常懵逼,但是不要怕。
    //多打印几个字符串看看,只是声明静态区字符串。
    static __NSConstantStringImpl __NSConstantStringImpl__var_folders_pr_mqg77fcj5hd0vp7mmx1lnf6h0000gn_T_TestBlock_555780_mi_0 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"nameBlock",9};
    
    
    //一个属性 _block 全局变量声明,之后会在ivar_list
    extern "C" unsigned long OBJC_IVAR_$_TestBlock$_block;
    
    //类TestBlock 的IMP 的list表
    struct TestBlock_IMPL {
        struct NSObject_IMPL NSObject_IVARS;
        NameBlock  _Nonnull _block;
    };
    
    // @property(nonatomic,copy)NameBlock block;
    // - (void)run:(NameBlock)block;
    /* @end */
    
    #pragma clang assume_nonnull end
    
    // @implementation TestBlock
    
    //function  void run:(){}  方法实现
    static void _I_TestBlock_run_(TestBlock * self, SEL _cmd, NameBlock  _Nonnull block) {
    
    //打印字符串string
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_mqg77fcj5hd0vp7mmx1lnf6h0000gn_T_TestBlock_555780_mi_0);
    }
    
    
    //_block 的getter方法 返回的是一个偏移指针的指针
    static void(* _I_TestBlock_block(TestBlock * self, SEL _cmd) )(NSString * _Nonnull){ return (*(NameBlock  _Nonnull *)((char *)self + OBJC_IVAR_$_TestBlock$_block)); }
    
    //一个声明 下面的方法会用到
    extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
    //_block  setter方法  objc_setProperty参数_OFFSETOFIVAR__(struct TestBlock, _block) 表示_block的偏移量,(id)block就是传递的block
    static void _I_TestBlock_setBlock_(TestBlock * self, SEL _cmd, NameBlock  _Nonnull block) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct TestBlock, _block), (id)block, 0, 1); }
    // @end
    

    block声明的setter 和getter方法,是单独c 类实现的。

    2.1 首先看看在block中没有引用self的情况

    - (void)run:(NameBlock)block{
        NSLog(@"nameBlock");
         NameBlock ablock = ^(NSString * _Nonnull name) {
            NSLog(@"%@",name);
        };
        ablock(@"123");
    }
    
    //编译c++代码
    struct __block_impl {
      void *isa; 
      int Flags;
      int Reserved;
      void *FuncPtr;
    };
    
    struct __TestBlock__run__block_impl_0 {
      struct __block_impl impl;
      struct __TestBlock__run__block_desc_0* Desc;
      __TestBlock__run__block_impl_0(void *fp, struct __TestBlock__run__block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __TestBlock__run__block_func_0(struct __TestBlock__run__block_impl_0 *__cself, NSString * _Nonnull name) {
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_mqg77fcj5hd0vp7mmx1lnf6h0000gn_T_TestBlock_2fd651_mi_4,name);
    
        }
    
    static struct __TestBlock__run__block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __TestBlock__run__block_desc_0_DATA = { 0, sizeof(struct __TestBlock__run__block_impl_0)};
    
    
    

    2.2 在看看在block中引用self的情况 我会写出和上边不一样的地方

    
    - (void)run:(NameBlock)block{
        
        NSLog(@"nameBlock");
        
        
        self.block = block;
        self.block = ^(NSString * _Nonnull name) {
            NSLog(@"%@",name);
            self.name = name;  //这里我们使用了self  造成的循环引用      
        };
        self.block(@"123");
    
    }
    
    
    struct __TestBlock__run__block_impl_0 {
      struct __block_impl impl;
      struct __TestBlock__run__block_desc_0* Desc;
      TestBlock *self;    //引用self 之后,多出来的。强引用self, self无法完全释放
      __TestBlock__run__block_impl_0(void *fp, struct __TestBlock__run__block_desc_0 *desc, TestBlock *_self, int flags=0) : self(_self) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __TestBlock__run__block_func_0(struct __TestBlock__run__block_impl_0 *__cself, NSString * _Nonnull name) {
      TestBlock *self = __cself->self; // bound by copy  //引用self 之后,多出来的。
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_mqg77fcj5hd0vp7mmx1lnf6h0000gn_T_TestBlock_e38d8e_mi_2,name);
            ((void (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)self, sel_registerName("setName:"), (NSString * _Nonnull)name);
        }
    
    //引用self 之后,多出来的。copy方法
    //之前声明的函数一值没有使用,引用self 之后会使用
    //extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int);
    extern "C" __declspec(dllexport) void _Block_object_dispose(const void *, const int);
    //多出的两个函数,assign 和 dispose 
    static void __TestBlock__run__block_copy_0(struct __TestBlock__run__block_impl_0*dst, struct __TestBlock__run__block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    //引用self 之后,多出来的。dispose方法
    static void __TestBlock__run__block_dispose_0(struct __TestBlock__run__block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static struct __TestBlock__run__block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __TestBlock__run__block_impl_0*, struct __TestBlock__run__block_impl_0*);//引用self 之后,多出来的。
      void (*dispose)(struct __TestBlock__run__block_impl_0*);//引用self 之后,多出来的。
    } __TestBlock__run__block_desc_0_DATA = { 0, sizeof(struct __TestBlock__run__block_impl_0), __TestBlock__run__block_copy_0, __TestBlock__run__block_dispose_0};
    //引用self 之后,多出来的两个参数。 __TestBlock__run__block_copy_0, __TestBlock__run__block_dispose_0
    

    下面的代码是runtime开源文件a1a2-blocktramps-arm64.s
    里面的一段代码:(也可以看看其他blcok文件,只是针对平台不同而已)

    //注释写的很清楚,我们再来仔细看看
        /*
         x0  == self 
         x17 == address of called trampoline's data (1 page before its code)
         lr  == original return address
         */
                                                //impl.isa = &_NSConcreteStackBlock
        mov  x1, x0                  //_cmd = self  x0 mov 从x0取值到x1 
        ldr  x0, [x17]                //self = block object 从[x17]取值impl到x0  
        ldr  x16, [x0, #16]        // tail call block->invoke 从x0<<16找impl.FuncPtr到x16  
        br   x16  //调用 x16
    
        
    

    更新中!!!!!!!!!

    相关文章

      网友评论

          本文标题:ios block的完全理解循环引用

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