ios|ios block的完全理解循环引用

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

#import NS_ASSUME_NONNULL_BEGINtypedef void(^NameBlock)(NSString *name); @interface TestBlock : NSObject @property(nonatomic,copy)NameBlock block; - (void)run:(NameBlock)block; @endNS_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//functionvoid 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); //_blocksetter方法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 = https://www.it610.com/article/{ 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 = https://www.it610.com/article/{ 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 movx1, x0//_cmd = selfx0 mov 从x0取值到x1 ldrx0, [x17]//self = block object 从[x17]取值impl到x0 ldrx16, [x0, #16]// tail call block->invoke 从x0<<16找impl.FuncPtr到x16 brx16//调用 x16

【ios|ios block的完全理解循环引用】更新中!!!!!!!!!

    推荐阅读