您现在的位置是:首页 > 文章详情

Objc Block实现分析

日期:2019-04-10点击:258

Objc Block实现分析

Block在iOS开发中使用的频率是很高的,使用的场景包括接口异步数据的回调(AFN)、UI事件的回调(BlockKits)、链式调用(Masonry)、容器数据的遍历回调(NSArray、NSDictionary),具体的用法以及使用Block的一些坑这里就不一一赘述了,本文会从源代码的层面分析我们常用的Block的底层实现原理,做到知其然知其所以然。

本文会从如下几个主题切入,层层递进分析Block的底层原理,还原Block本来的面目

  • 无参数无返回值,不使用变量的Block分析
  • 使用自动变量的Block分析
  • 使用__block修饰的变量的Block分析
  • Block引用分析

无参数无返回值,不使用变量的Block分析

有如下的源代码,创建一个简单的block,在block做的处理是打印一个字符串,然后执行这个block。接下来会从源码入手对此进行分析:block是如何执行的

// 无参数无返回值的Block分析 int main(int argc, const char * argv[]) { void (^blk) (void) = ^{ printf("block invoke"); }; blk(); return 0; } // 输出: block invoke 

使用clang -rewrite-objc main.m命令重写为C++语法的源文件。从重写后的代码中看到,一个简单block定义和调用的代码变成了大几十行,不过该源代码还算是相对简单的,我们从main函数入口逐步的进行分析:

main函数中创建__main_block_impl_0类型的实例

__main_block_impl_0结构体是什么鬼呢?我们看__main_block_impl_0结构体的实现,包含了__block_impl结构体和__main_block_desc_0结构体指针,为了方便,现在页先不用管__main_block_desc_0结构体,目前他还没有真正的使用到,后面讲到的__block修饰自动变量以及Block对对象的引用关系,设计到内存的拷贝和释放的时候会使用到该变量。__block_impl结构体包含了4个成员,为了简单分析,我们只关注其中的FuncPtr成员的FuncPtr成员,这个也是最重要的成员,其它的先忽略不计。

__main_block_impl_0实例的初始化参数

第一个是__main_block_func_0函数的地址,__main_block_func_0函数是什么呢,其实就是Block执行的方法,可以看到里面的实现就是一个简单的打印而已,和我们定义在block中的实现一样的,该参数用于初始化impl成员的。至于第二个参数先不管,在这个例子木有用到。

__main_block_impl_0实例的调用

创建了__main_block_impl_0结构体类型的实例之后,接下来就是获取到里面的FuncPtr指针指向的方法进行调用,参数是__main_block_impl_0结构体类型的实例本身,上一步创建步骤可知,FuncPtr指针是一个函数指针指向__main_block_func_0函数的地址,所以这里本质上就是__main_block_func_0函数的调用,结构是打印字符串"block invoke",一个简单的block执行孙然在代码量上增加了,其实也不算复杂。

注意点:(__block_impl *)blk这里做了一个强制转换,blk是__main_block_impl_0类型的实例的指针,根据结构体的内存布局规则,该结构体的第一个成员为__block_impl 类型,所以可以强制转换为__block_impl *类型。

由上面的分析,可以得出如下的结论:使用变量的Block调用本质上是使用函数指针调用函数

 // 使用`clang -rewrite-objc main.m`命令重写为C++语法的源文件,对结果分析如下 // __block_impl结构体 struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; // `__main_block_impl_0`是block的结构体,包含了两个成员__block_impl类型的impl成员以及__main_block_desc_0类型的Desc成员;一个构造方法__main_block_impl_0,构造方法中初始化impl成员和Desc成员 struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; // Block执行的方法 static void __main_block_func_0(struct __main_block_impl_0 *__cself) { printf("block invoke"); } static struct __main_block_desc_0 { size_t reserved; size_t Block_size; } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; // 重写后的入口函数main int main(int argc, const char * argv[]) { // 创建__main_block_impl_0类型的实例,名称为blk,这里把改实例强制转换为`void(funcPtr *)(void)`型的方法指针,不懂为何,因为在下一步会把该方法指针强制转换为对应的结构体,也就是说`void(funcPtr *)(void)`型的方法指针并没有真实的使用到。 void (*blk) (void) = ((void (*)())&__main_block_impl_0( (void *)__main_block_func_0, &__main_block_desc_0_DATA)); // blk是`__main_block_impl_0`类型的实例的指针,根绝结构体的内存布局规则,该结构体的第一个成员为`__block_impl` 类型,可以强制转换为`__block_impl *`类型,获取FuncPtr函数指针,强制转换为`(void (*)(__block_impl *))`类型的函数指针,然后执行这个函数指针对应的函数,参数为blk // 由上面的步骤可知,Block的调用本质上是使用函数指针调用函数 ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); return 0; } 

使用自动变量的Block分析

有如下的源代码,创建一个简单的block,在block做的处理是打印一个字符串,使用到外部的自动变量,然后执行这个block。接下来会从源码入手对此进行分析:block是如何使用外部的自动变量的

// 使用变量的Block分析 // 源代码 int main(int argc, const char * argv[]) { int age = 100; const char *name = "zyt"; void (^blk) (void) = ^{ printf("block invoke age = %d age = %s", age, name); }; age = 101; blk(); return 0; } // 输出: block invoke age = 100 age = zyt 

使用clang -rewrite-objc main.m命令重写为C++语法的源文件。对比上一个的数据结构方法和调用步骤大体上是一样的,只针对变化的地方进行分析

__main_block_impl_0结构体的变化

增加了两个成员:int类型的age成员、char*类型的name成员,用于保存外部变量的值,在初始化的时候就会使用自动变量的值初始化这两个成员的值

调用的变化

__main_block_func_0函数的参数struct __main_block_impl_0 *__cself在这个例子有使用到了,因为两个自动变量对应的值被保存在__main_block_impl_0结构体中了,方法中有使用到这两个变量,直接从__main_block_impl_0结构体中获取这两个值,但是这两个值是独立于自动变量的存在了

由上面的分析,可以得出如下的结论:使用变量的Block调用本质上是使用函数指针调用函数,参数是保存在block的结构体中的,并且保存的值而不是引用

 // 使用`clang -rewrite-objc main.m`命令重写为C++语法的源文件,对结果分析如下 // __block_impl结构体 struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; // __main_block_impl_0,包含了四个成员__block_impl类型的impl成员、__main_block_desc_0类型的Desc成员、int类型的age成员、char*类型的name成员;一个构造方法__main_block_impl_0,构造方法中初始化impl成员、Desc成员、age成员和name成员,比起上一个改结构体多了两个成员,用于保存外部变量的值 struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; const char *name; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, const char *_name, int flags=0) : age(_age), name(_name) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; // Block执行的方法 static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int age = __cself->age; // bound by copy const char *name = __cself->name; // bound by copy printf("block invoke age = %d age = %s", age, name); } static struct __main_block_desc_0 { size_t reserved; size_t Block_size; } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; // 重写后的入口函数main // 由上面的步骤可知,使用变量的Block调用本质上是使用函数指针调用函数,参数是保存在block的结构体中的,并且保存的值而不是引用 int main(int argc, const char * argv[]) { int age = 100; const char *name = "zyt"; void (*blk) (void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age, name)); age = 101; ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); return 0; } 

使用__block修饰的变量的Block分析

有如下的源代码,有个__block修饰的int类型的自动变量age,在block和变量age的作用域中分别作了修改,从输入的结果看看是两次都生效了,接下来会从源码入手对此进行分析:block中是如何处理__block修饰的自动变量,该自动变量的内存是如何变化的

// 源代码 int main(int argc, const char * argv[]) { __block int age = 100; const char *name = "zyt"; void (^blk) (void) = ^{ age += 2; printf("block invoke age = %d age = %s", age, name); }; age += 1; blk(); } // 输出 : // block invoke age = 103 age = zyt 

使用clang -rewrite-objc main.m命令重写为C++语法的源文件,对结果分析如下的注释,该转换后的代码做了些许的调整,包括代码的缩进和添加了结构体声明,使得该代码可以直接运行

 // clang改写后的代码如下,以下代码是经过调整可以直接运行的 struct __main_block_desc_0; // __block_impl结构体 struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; // `__block`修饰的变量对应的结构体,里面包含了该变量的原始值也就是age成员,另外还有一个奇怪的`__forwarding`成员,稍后我们会分析它的用处 struct __Block_byref_age_0 { void *__isa; __Block_byref_age_0 *__forwarding; int __flags; int __size; int age; }; // `__main_block_impl_0`结构体包含了四个成员`__block_impl`类型的impl成员、`__main_block_desc_0`类型的Desc成员、`__Block_byref_age_0 *`类型的age成员、char*类型的name成员;一个构造方法`__main_block_impl_0`,构造方法中初始化impl成员、Desc成员、age成员和name成员,比起上一个结构体的变化是age的类型变为了包装自动变量的结构体了 struct __main_block_impl_0 { __block_impl impl; __main_block_desc_0* Desc; const char *name; __Block_byref_age_0 *age; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_name, __Block_byref_age_0 *_age, int flags=0) : name(_name), age(_age->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; // Block执行的方法 static void __main_block_func_0(struct __main_block_impl_0 *__cself) { __Block_byref_age_0 *age = __cself->age; // bound by ref const char *name = __cself->name; // bound by copy (age->__forwarding->age) += 2; printf("block invoke age = %d age = %s", (age->__forwarding->age), name); } // Block拷贝函数 static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) { _Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/); } // Block销毁函数 static void __main_block_dispose_0(struct __main_block_impl_0*src) { _Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/); } 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}; // 重写后的入口函数main int main(int argc, const char * argv[]) { __attribute__((__blocks__(byref))) __Block_byref_age_0 age = { (void*)0, (__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 100}; const char *name = "zyt"; struct __main_block_impl_0 blockImpl = __main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, name, (__Block_byref_age_0 *)&age, 570425344); void (*blk) (void) = ((void (*)())&blockImpl); (age.__forwarding->age) += 1; ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); } 

__block修饰自动变量转换为C++代码的结构体关系图:
__block修饰自动变量转换为C++代码的结构体关系图

该重写的代码大部分和上面分析过的例子代码是类似,发现增加了两个处理方法Block拷贝函数__main_block_copy_0和Block销毁函数__main_block_dispose_0,这两个函数会保存在__main_block_desc_0结构体的copydispose成员中;另外添加了一个__Block_byref_age_0结构体类型用户处理__block修饰的自动变量。以下针对这两点从源代码的角度进行一个分析

__main_block_copy_0方法中调用到的_Block_object_assign可以在 runtime.c 这里找到 ,主要看下_Block_object_assign方法里面的处理逻辑

  • flags参数值为8,是BLOCK_FIELD_IS_BYREF枚举对应的值,会走到_Block_byref_assign_copy方法的调用步骤
  • _Block_byref_assign_copy方法会在在堆上创建Block_byref对象,也就是Block对象,并且把栈上和堆上的Block对象的forwarding属性值都修改为指向堆上的Block对象,这样使用两个对象的修改值都会修改为同一个地方

栈上的__block自动变量__forwarding指向关系以及拷贝到堆上之后__forwarding指向关系如下图所示
栈上的自动变量指向关系以及拷贝到堆上之后指向关系如下图所示

具体使用到的代码和对应的注释如下:

/* * When Blocks or Block_byrefs hold objects then their copy routine helpers use this entry point * to do the assignment. */ void _Block_object_assign(void *destAddr, const void *object, const int flags) { //printf("_Block_object_assign(*%p, %p, %x)\n", destAddr, object, flags); if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) { if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) { _Block_assign_weak(object, destAddr); } else { // do *not* retain or *copy* __block variables whatever they are _Block_assign((void *)object, destAddr); } } // 代码会走到这个分支中,调用方法`_Block_byref_assign_copy` else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF) { // copying a __block reference from the stack Block to the heap // flags will indicate if it holds a __weak reference and needs a special isa _Block_byref_assign_copy(destAddr, object, flags); } // (this test must be before next one) else if ((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK) { // copying a Block declared variable from the stack Block to the heap _Block_assign(_Block_copy_internal(object, flags), destAddr); } // (this test must be after previous one) else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) { //printf("retaining object at %p\n", object); _Block_retain_object(object); //printf("done retaining object at %p\n", object); _Block_assign((void *)object, destAddr); } } /* * Runtime entry points for maintaining the sharing knowledge of byref data blocks. * * A closure has been copied and its fixup routine is asking us to fix up the reference to the shared byref data * Closures that aren't copied must still work, so everyone always accesses variables after dereferencing the forwarding ptr. * We ask if the byref pointer that we know about has already been copied to the heap, and if so, increment it. * Otherwise we need to copy it and update the stack forwarding pointer * XXX We need to account for weak/nonretained read-write barriers. */ static void _Block_byref_assign_copy(void *dest, const void *arg, const int flags) { struct Block_byref **destp = (struct Block_byref **)dest; struct Block_byref *src = (struct Block_byref *)arg; //printf("_Block_byref_assign_copy called, byref destp %p, src %p, flags %x\n", destp, src, flags); //printf("src dump: %s\n", _Block_byref_dump(src)); if (src->forwarding->flags & BLOCK_IS_GC) { ; // don't need to do any more work } else if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) { //printf("making copy\n"); // src points to stack bool isWeak = ((flags & (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)) == (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)); // if its weak ask for an object (only matters under GC) struct Block_byref *copy = (struct Block_byref *)_Block_allocator(src->size, false, isWeak); copy->flags = src->flags | _Byref_flag_initial_value; // non-GC one for caller, one for stack // copy是拷贝到堆上的Block_byref类型对象,scr是原来的Block_byref类型对象,两者的forwarding成员都指向到堆上的Block_byref类型对象也就是copy,这样不管是在栈上修改__block修饰的变量(age.age = 102调用)还是在堆上修改__block修饰的变量() copy->forwarding = copy; // patch heap copy to point to itself (skip write-barrier) src->forwarding = copy; // patch stack to point to heap copy copy->size = src->size; if (isWeak) { copy->isa = &_NSConcreteWeakBlockVariable; // mark isa field so it gets weak scanning } if (src->flags & BLOCK_HAS_COPY_DISPOSE) { // Trust copy helper to copy everything of interest // If more than one field shows up in a byref block this is wrong XXX copy->byref_keep = src->byref_keep; copy->byref_destroy = src->byref_destroy; (*src->byref_keep)(copy, src); } else { // just bits. Blast 'em using _Block_memmove in case they're __strong _Block_memmove( (void *)&copy->byref_keep, (void *)&src->byref_keep, src->size - sizeof(struct Block_byref_header)); } } // already copied to heap else if ((src->forwarding->flags & BLOCK_NEEDS_FREE) == BLOCK_NEEDS_FREE) { latching_incr_int(&src->forwarding->flags); } // assign byref data block pointer into new Block _Block_assign(src->forwarding, (void **)destp); } 

Block引用分析

Block强引用分析

// 定义类YTObject @interface YTObject : NSObject @property (nonatomic, strong) NSString *name; @property (nonatomic, copy) void (^blk)(void); - (void)testReferenceSelf; @end @implementation YTObject - (void)testReferenceSelf { self.blk = ^ { // 这里不管是使用self.name还是_name,从clang重写的代码上看,处理方式是一样的 printf("self.name = %s", self.name.UTF8String); }; self.blk(); } - (void)dealloc { NSLog(@"==dealloc=="); } @end // 使用YTObject int main(int argc, const char * argv[]) { YTObject *obj = [YTObject new]; obj.name = @"hello"; [obj testReferenceSelf]; return 0; } // 输出 : // self.name = hello 

使用clang -rewrite-objc main.m命令重写为C++语法的源文件如下

 static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[6]; } _OBJC_$_INSTANCE_METHODS_YTObject __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_objc_method), 6, {{(struct objc_selector *)"testReferenceSelf", "v16@0:8", (void *)_I_YTObject_testReferenceSelf}, {(struct objc_selector *)"dealloc", "v16@0:8", (void *)_I_YTObject_dealloc}, {(struct objc_selector *)"name", "@16@0:8", (void *)_I_YTObject_name}, {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_YTObject_setName_}, {(struct objc_selector *)"blk", "@?16@0:8", (void *)_I_YTObject_blk}, {(struct objc_selector *)"setBlk:", "v24@0:8@?16", (void *)_I_YTObject_setBlk_}} }; struct __YTObject__testReferenceSelf_block_impl_0 { struct __block_impl impl; struct __YTObject__testReferenceSelf_block_desc_0* Desc; YTObject *self; __YTObject__testReferenceSelf_block_impl_0(void *fp, struct __YTObject__testReferenceSelf_block_desc_0 *desc, YTObject *_self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __YTObject__testReferenceSelf_block_func_0(struct __YTObject__testReferenceSelf_block_impl_0 *__cself) { YTObject *self = __cself->self; // bound by copy printf("self.name = %s", ((const char * _Nullable (*)(id, SEL))(void *)objc_msgSend)((id)((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("name")), sel_registerName("UTF8String"))); } static void __YTObject__testReferenceSelf_block_copy_0(struct __YTObject__testReferenceSelf_block_impl_0*dst, struct __YTObject__testReferenceSelf_block_impl_0*src) { _Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/); } static void __YTObject__testReferenceSelf_block_dispose_0(struct __YTObject__testReferenceSelf_block_impl_0*src) { _Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/); } static struct __YTObject__testReferenceSelf_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __YTObject__testReferenceSelf_block_impl_0*, struct __YTObject__testReferenceSelf_block_impl_0*); void (*dispose)(struct __YTObject__testReferenceSelf_block_impl_0*); } __YTObject__testReferenceSelf_block_desc_0_DATA = { 0, sizeof(struct __YTObject__testReferenceSelf_block_impl_0), __YTObject__testReferenceSelf_block_copy_0, __YTObject__testReferenceSelf_block_dispose_0}; static void _I_YTObject_testReferenceSelf(YTObject * self, SEL _cmd) { ((void (*)(id, SEL, void (*)()))(void *)objc_msgSend)((id)self, sel_registerName("setBlk:"), ((void (*)())&__YTObject__testReferenceSelf_block_impl_0((void *)__YTObject__testReferenceSelf_block_func_0, &__YTObject__testReferenceSelf_block_desc_0_DATA, self, 570425344))); ((void (*(*)(id, SEL))())(void *)objc_msgSend)((id)self, sel_registerName("blk"))(); } int main(int argc, const char * argv[]) { YTObject *obj = ((YTObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("YTObject"), sel_registerName("new")); ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)obj, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_fk_19cr58zj0f7f19001k_mxzlm0000gp_T_main_21b52d_mii_1); ((void (*)(id, SEL))(void *)objc_msgSend)((id)obj, sel_registerName("testReferenceSelf")); return 0; } 

Block引用对象转换为C++代码的结构体关系图:
Block引用对象转换为C++代码的结构体关系图

从类图上可以明显的看到__YTObject__testReferenceSelf_block_impl_0YTObject之间有循环依赖的关系,这样NSLog(@"==dealloc==");这段代码最终是没有调用到,也就是这里会出现Block循环引用导致内存泄漏问题

Block弱引用分析

Block的循环引用问题其中一种解决方案是可以使用weakself来解除这种强引用关系,防止内存的泄漏,代码的改造如下

@interface YTObject : NSObject @property (nonatomic, strong) NSString *name; @property (nonatomic, copy) void (^blk)(void); - (void)testReferenceSelf; @end @implementation YTObject - (void)testReferenceSelf { __weak typeof(self) weakself = self; self.blk = ^ { __strong typeof(self) strongself = weakself; // 这里不管是使用self.name还是_name,从clang重写的代码上看,处理方式是一样的 printf("self.name = %s\n", strongself.name.UTF8String); }; self.blk(); } - (void)dealloc { printf("==dealloc=="); } @end // 使用YTObject int main(int argc, const char * argv[]) { YTObject *obj = [YTObject new]; obj.name = @"hello"; [obj testReferenceSelf]; return 0; } 

添加了weak之后需要使用clang -rewrite-objc -fobjc-arc -fobjc-runtime=macosx-10.14 main.mm这个命令才能够重写为C++语言对应的代码,重写后的代码如下

static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[6]; } _OBJC_$_INSTANCE_METHODS_YTObject __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_objc_method), 6, {{(struct objc_selector *)"testReferenceSelf", "v16@0:8", (void *)_I_YTObject_testReferenceSelf}, {(struct objc_selector *)"dealloc", "v16@0:8", (void *)_I_YTObject_dealloc}, {(struct objc_selector *)"name", "@16@0:8", (void *)_I_YTObject_name}, {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_YTObject_setName_}, {(struct objc_selector *)"blk", "@?16@0:8", (void *)_I_YTObject_blk}, {(struct objc_selector *)"setBlk:", "v24@0:8@?16", (void *)_I_YTObject_setBlk_}} }; struct __YTObject__testReferenceSelf_block_impl_0 { struct __block_impl impl; struct __YTObject__testReferenceSelf_block_desc_0* Desc; YTObject *const __weak weakself; __YTObject__testReferenceSelf_block_impl_0(void *fp, struct __YTObject__testReferenceSelf_block_desc_0 *desc, YTObject *const __weak _weakself, int flags=0) : weakself(_weakself) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __YTObject__testReferenceSelf_block_func_0(struct __YTObject__testReferenceSelf_block_impl_0 *__cself) { YTObject *const __weak weakself = __cself->weakself; // bound by copy __attribute__((objc_ownership(strong))) typeof(self) strongself = weakself; printf("self.name = %s\n", ((const char * _Nullable (*)(id, SEL))(void *)objc_msgSend)((id)((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)strongself, sel_registerName("name")), sel_registerName("UTF8String"))); } static void __YTObject__testReferenceSelf_block_copy_0(struct __YTObject__testReferenceSelf_block_impl_0*dst, struct __YTObject__testReferenceSelf_block_impl_0*src) { _Block_object_assign((void*)&dst->weakself, (void*)src->weakself, 3/*BLOCK_FIELD_IS_OBJECT*/); } static void __YTObject__testReferenceSelf_block_dispose_0(struct __YTObject__testReferenceSelf_block_impl_0*src) { _Block_object_dispose((void*)src->weakself, 3/*BLOCK_FIELD_IS_OBJECT*/); } static struct __YTObject__testReferenceSelf_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __YTObject__testReferenceSelf_block_impl_0*, struct __YTObject__testReferenceSelf_block_impl_0*); void (*dispose)(struct __YTObject__testReferenceSelf_block_impl_0*); } __YTObject__testReferenceSelf_block_desc_0_DATA = { 0, sizeof(struct __YTObject__testReferenceSelf_block_impl_0), __YTObject__testReferenceSelf_block_copy_0, __YTObject__testReferenceSelf_block_dispose_0}; static void _I_YTObject_testReferenceSelf(YTObject * self, SEL _cmd) { __attribute__((objc_ownership(weak))) typeof(self) weakself = self; __YTObject__testReferenceSelf_block_impl_0 blockImpl = __YTObject__testReferenceSelf_block_impl_0((void *)__YTObject__testReferenceSelf_block_func_0, &__YTObject__testReferenceSelf_block_desc_0_DATA, weakself, 570425344) ((void (*)(id, SEL, void (*)()))(void *)objc_msgSend)((id)self, s el_registerName("setBlk:"), ((void (*)())&blockImpl)); ((void (*(*)(id, SEL))())(void *)objc_msgSend)((id)self, sel_registerName("blk"))(); } int main(int argc, const char * argv[]) { YTObject *obj = ((YTObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("YTObject"), sel_registerName("new")); ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)obj, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_fk_19cr58zj0f7f19001k_mxzlm0000gp_T_main_6f39dd_mii_0); ((void (*)(id, SEL))(void *)objc_msgSend)((id)obj, sel_registerName("testReferenceSelf")); return 0; } 

从上面的代码中可以看到__YTObject__testReferenceSelf_block_impl_0结构体中weakself成员是一个__weak修饰的YTObject类型对象,也就是说__YTObject__testReferenceSelf_block_impl_0YTObject的依赖是弱依赖。weak修饰变量是在runtime中进行处理的,在YTObject对象的Dealloc方法中会调用weak引用的处理方法,从weak_table中寻找弱引用的依赖对象,进行清除处理,可以查看Runtime源码中objc_object::clearDeallocating该方法的处理逻辑,另外关于__weak修饰的变量的详细处理可以查看Runtime相关的知识

Block弱引用对象转换为C++代码的结构体关系图:
Block弱引用对象转换为C++代码的结构体关系图

关于具体的weakSelf和strongSelf可以参考这篇文章深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用中的描述

weakSelf 是为了block不持有self,避免Retain Circle循环引用。在 Block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。 strongSelf的目的是因为一旦进入block执行,假设不允许self在这个执行过程中释放,就需要加入strongSelf。block执行完后这个strongSelf 会自动释放,没有不会存在循环引用问题。如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。

结束

以上就是关于Block底层实现的一些分析,不妥之处敬请指教

参考

深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用

原文链接:https://my.oschina.net/FEEDFACF/blog/3034529
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章