ios block的完全理解循环引用

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

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

#import 

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的完全理解循环引用)