Block 底层实现原理
在理解 Block 的底层实现原理的过程中,可以通过 clang
命令将一个 block 例子转换为 C
代码,从而解读其具体的代码实现逻辑。
clang main.m -rewrite-objc -o dest.cpp
上面的命令,是将 main.m
文件源码转换为 C
语言代码并存储在 dest.cpp
文件中。
理解 block 的实现
-
第一步,准备一个
main.m
文件,包含一个 block 定义。int main(int argc, char * argv[]) { void (^test)() = ^(){ }; test(); }
-
运行
clang main.m -rewrite-objc -o dest.cpp
命令,查看转换后的代码。#ifndef __OBJC2__ #define __OBJC2__ #endif struct objc_selector; struct objc_class; struct __rw_objc_super { struct objc_object *object; struct objc_object *superClass; __rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {} }; #ifndef _REWRITER_typedef_Protocol typedef struct objc_object Protocol; #define _REWRITER_typedef_Protocol #endif #define __OBJC_RW_DLLIMPORT extern __OBJC_RW_DLLIMPORT void objc_msgSend(void); __OBJC_RW_DLLIMPORT void objc_msgSendSuper(void); __OBJC_RW_DLLIMPORT void objc_msgSend_stret(void); __OBJC_RW_DLLIMPORT void objc_msgSendSuper_stret(void); __OBJC_RW_DLLIMPORT void objc_msgSend_fpret(void); __OBJC_RW_DLLIMPORT struct objc_class *objc_getClass(const char *); __OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass(struct objc_class *); __OBJC_RW_DLLIMPORT struct objc_class *objc_getMetaClass(const char *); __OBJC_RW_DLLIMPORT void objc_exception_throw( struct objc_object *); __OBJC_RW_DLLIMPORT int objc_sync_enter( struct objc_object *); __OBJC_RW_DLLIMPORT int objc_sync_exit( struct objc_object *); __OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *); #ifdef _WIN64 typedef unsigned long long _WIN_NSUInteger; #else typedef unsigned int _WIN_NSUInteger; #endif #ifndef __FASTENUMERATIONSTATE struct __objcFastEnumerationState { unsigned long state; void **itemsPtr; unsigned long *mutationsPtr; unsigned long extra[5]; }; __OBJC_RW_DLLIMPORT void objc_enumerationMutation(struct objc_object *); #define __FASTENUMERATIONSTATE #endif #ifndef __NSCONSTANTSTRINGIMPL struct __NSConstantStringImpl { int *isa; int flags; char *str; #if _WIN64 long long length; #else long length; #endif }; #ifdef CF_EXPORT_CONSTANT_STRING extern "C" __declspec(dllexport) int __CFConstantStringClassReference[]; #else __OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[]; #endif #define __NSCONSTANTSTRINGIMPL #endif #ifndef BLOCK_IMPL #define BLOCK_IMPL struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; // Runtime copy/destroy helper functions (from Block_private.h) #ifdef __OBJC_EXPORT_BLOCKS 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); extern "C" __declspec(dllexport) void *_NSConcreteGlobalBlock[32]; extern "C" __declspec(dllexport) void *_NSConcreteStackBlock[32]; #else __OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int); __OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int); __OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32]; __OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32]; #endif #endif #define __block #define __weak #include <stdarg.h> struct __NSContainer_literal { void * *arr; __NSContainer_literal (unsigned int count, ...) { va_list marker; va_start(marker, count); arr = new void *[count]; for (unsigned i = 0; i < count; i++) arr[i] = va_arg(marker, void *); va_end( marker ); }; ~__NSContainer_literal() { delete[] arr; } }; extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void); extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *); struct __AtAutoreleasePool { __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();} ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);} void * atautoreleasepoolobj; }; #define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER) 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; } }; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { } 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)}; int main(int argc, char * argv[]) { void (*test)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); ((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)((__block_impl *)test); } static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
-
比较
main
函数,理解关键代码。void (^test)() = ^(){ };
同下面的代码相对应void (*test)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
test();
同((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)((__block_impl *)test);
相对应。想理解这两段代码,要先看相关的结构体及函数。
-
理解相关的结构体及函数
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; } };
结构体
__main_block_impl_0
包含一个__block_impl
类型(结构体)的成员变量,一个指向__main_block_desc_0
类型(结构体)的指针,以及一个构造函数。这个结构体的构造函数有三个参数,在初始化过程中,除了使用三个参数赋值外,还将
_NSConcreteStackBlock
的地址赋值给了impl.isa
,查找代码可知_NSConcreteStackBlock
是长度为 32 的指针数组。impl.isa = &_NSConcreteStackBlock;
所以
impl.isa
指向一个数组,而该数组保存指针。struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
结构体
__block_impl
包含四个成员变量,两个指针,两个int
类型的变量。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_desc_0
包含两个成员变量,一个表示 block 所需内存的大小,一个是保留变量。这里直接定义了一个静态的结构体变量
__main_block_desc_0_DATA
,并且Block_size
的值初始化为结构体__main_block_impl_0
的大小。static void __main_block_func_0(struct __main_block_impl_0 *__cself) { }
该静态函数实际就是 block 中要执行的代码任务,这里没有要执行的代码。
-
理解 block 的定义和执行
上一步理解了相关的结构体及函数后,再来理解 block 的定义及执行。
void (*test)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
拆分该代码如下:
//初始化一个 __main_block_impl_0 结构体 //参数为生成的静态函数 __main_block_func_0 以及静态变量 __main_block_desc_0_DATA 的地址 struct __main_block_impl_0 temp = __main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA); void (*test)() = ((void (*)())&temp);
可见,block 的定义,最终是获得了一个指向一个结构体的指针。
((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)((__block_impl *)test);
这里要注意的是
test
实际是指向__main_block_impl_0
结构体的指针,而这里总是将其强制转转换为指向__block_impl
结构体的指针,这样是没有问题的,因为__main_block_impl_0
的第一个成员变量就是__block_impl
类型的,所以两个变量的地址是一致的。而后,获取__block_impl
的FuncPtr
成员变量,并转换为函数地址,该地址就是生成的静态函数__main_block_func_0
的地址,最终执行该静态函数来完成 block 中的任务。
综上所述,定义一个 block 任务,实际是生成了一个指针,同时会生成一个包含该任务的静态函数,并且该指针指向该静态函数,而 block 的调用,则是使用该指针调用其所指向的静态函数。
block 捕获局部变量
-
准备一个
main1.m
文件,如下#include <stdio.h> int main(int argc, char * argv[]) { int value1 = 2; int value2 = 3; void (^test)() = ^() { int blockValue1 = value1; int blockValue2 = value2 + 1; printf("blockValue1 = %i, blockValue2 = %i \n",blockValue1,blockValue2); }; test(); }
在函数中定义两个局部变量,并在 block 中进行使用。
-
运行
clang main1.m -rewrite-objc -o dest1.cpp
命令,查看转换后的代码dest1.cpp
与dest.cpp
文件的区别。struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
结构体
__block_impl
未发生改变struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int value1; int value2; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _value1, int _value2, int flags=0) : value1(_value1), value2(_value2) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
结构体
__main_block_impl_0
中多了两个和局部变量同名的成员变量,并且构造函数中也多了两个参数用来对这两个成员变量赋值。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_desc_0
未发生变化static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int value1 = __cself->value1; // bound by copy int value2 = __cself->value2; // bound by copy int blockValue1 = value1; int blockValue2 = value2 + 1; printf("blockValue1 = %i, blockValue2 = %i \n",blockValue1,blockValue2); }
从生成的静态函数可知,当其执行时,所获取的局部变量实际是
__main_block_impl_0
结构体中保存的成员变量。int main(int argc, char * argv[]) { int value1 = 2; int value2 = 3; void (*test)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, value1, value2)); ((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)((__block_impl *)test); }
该函数同样是定义了一个指向
__main_block_impl_0
结构体类型变量的指针,而其构造函数传递了局部变量,并且是值传递,所以在该定义之后再去修改局部变量,并不会影响 block 回调执行时的变量值。 -
执行
gcc main1.m -o main1.out
编译命令得到
main1.out
文件后,执行./main1.out
,得到如下结果:blockValue1 = 2, blockValue2 = 4
综上所述,block 的局部变量的使用,是将局部变量保存到结构体中,而在 block 执行时,再从结构体中获取保存的变量。
block 参数及返回值
-
准备一个
main2.m
文件,如下#import <Foundation/Foundation.h> #include <stdio.h> int main(int argc, char * argv[]) { int value1 = 1; NSNumber *number1 = [NSNumber numberWithInt:2]; NSNumber * (^test)(int,NSNumber *) = ^(int value2,NSNumber *number2) { int blockValue = value1; NSNumber *blockNumber = number1; int result = blockValue + value2 + blockNumber.intValue + number2.intValue; number2 = [NSNumber numberWithInt:100]; return [NSNumber numberWithInt:result]; }; int value2 = 3; NSNumber *number2 = [NSNumber numberWithInt:4]; NSNumber *number = test(value2,number2); }
定义的 block 拥有一个 NSNumber 类型的返回值,以及两个参数,比较参数使用和变量的捕获。
-
执行
clang main2.m -rewrite-objc -o dest2.cpp
命令,查看转换后的代码dest2.cpp
与dest.cpp
文件的区别。struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
结构体
__block_impl
未发生改变struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int value1; NSNumber *number1; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _value1, NSNumber *_number1, int flags=0) : value1(_value1), number1(_number1) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
在 block 中引用了外部 OC 对象,同样会在
__main_block_impl_0
中添加一个同名的对象变量,并通过构造函数进行赋值。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); static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) { _Block_object_assign((void*)&dst->number1, (void*)src->number1, 3/*BLOCK_FIELD_IS_OBJECT*/); } static void __main_block_dispose_0(struct __main_block_impl_0*src) { _Block_object_dispose((void*)src->number1, 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};
结构体
__main_block_desc_0
同样发生了变化,此处多了两个函数指针成员变量,分别在 block 拷贝到堆区以及释放时调用。static NSNumber * _Nonnull __main_block_func_0(struct __main_block_impl_0 *__cself, int value2, NSNumber *number2) { int value1 = __cself->value1; // bound by copy NSNumber *number1 = __cself->number1; // bound by copy int blockValue = value1; NSNumber *blockNumber = number1; int result = blockValue + value2 + ((int (*)(id, SEL))(void *)objc_msgSend)((id)blockNumber, sel_registerName("intValue")) + ((int (*)(id, SEL))(void *)objc_msgSend)((id)number2, sel_registerName("intValue")); number2 = ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 100); return ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), (int)result); }
静态函数相应的多了两个参数,并且在函数最后对 number2 进行了修改,虽然其修改并不影响原参数。而在 block 中不可修改 number1 是因为在该函数中 number1 和 block 外的 number1 并不是同一个变量。
int main(int argc, char * argv[]) { int value1 = 1; NSNumber *number1 = ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 2); NSNumber * (*test)(int,NSNumber *) = ((NSNumber * _Nonnull (*)(int, NSNumber *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, value1, number1, 570425344)); int value2 = 3; NSNumber *number2 = ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 4); NSNumber *number = ((NSNumber *(*)(__block_impl *, int, NSNumber *))((__block_impl *)test)->FuncPtr)((__block_impl *)test, value2, number2); }
同样,block 的调用,就是静态函数的执行。
block 中修改外部变量
-
准备一个
main3.m
文件,如下#import <Foundation/Foundation.h> #include <stdio.h> static struct testStruct { int num; void (^test)(void); } testS = { 10, nil }; int main(int argc, char * argv[]) { int value1 = 1; __block int value2 = 2; NSNumber *number1 = [NSNumber numberWithInt:3]; __block NSNumber *number2 = [NSNumber numberWithInt:4]; testS.test = ^() { int blockValue = testS.num; int sum = value1 + value2 + number1.intValue + number2.intValue; value2 = 5; number2 = [NSNumber numberWithInt:6]; }; value2 = 10; number2 = [NSNumber numberWithInt:20]; testS.test(); }
对比基本类型和 OC 对象,并且查看全局静态变量在 block 中的使用情况。
-
执行
clang main3.m -rewrite-objc -o dest3.cpp
命令,查看转换后的代码dest3.cpp
与dest.cpp
文件的区别。int main(int argc, char * argv[]) { int value1 = 1; __attribute__((__blocks__(byref))) __Block_byref_value2_0 value2 = {(void*)0,(__Block_byref_value2_0 *)&value2, 0, sizeof(__Block_byref_value2_0), 2}; NSNumber *number1 = ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 3); __attribute__((__blocks__(byref))) __Block_byref_number2_1 number2 = {(void*)0,(__Block_byref_number2_1 *)&number2, 33554432, sizeof(__Block_byref_number2_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 4)}; testS.test = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, value1, number1, (__Block_byref_value2_0 *)&value2, (__Block_byref_number2_1 *)&number2, 570425344)); (value2.__forwarding->value2) = 10; (number2.__forwarding->number2) = ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 20); ((void (*)(__block_impl *))((__block_impl *)testS.test)->FuncPtr)((__block_impl *)testS.test); }
首先对比
main
函数,发现使用__block
字段修饰的变量都封装成了结构体,并同普通变量一起作为参数传递给了__main_block_impl_0
结构体的构造函数。struct __Block_byref_value2_0 { void *__isa; __Block_byref_value2_0 *__forwarding; int __flags; int __size; int value2; }; struct __Block_byref_number2_1 { void *__isa; __Block_byref_number2_1 *__forwarding; int __flags; int __size; void (*__Block_byref_id_object_copy)(void*, void*); void (*__Block_byref_id_object_dispose)(void*); NSNumber *number2; };
封装实例对象的结构体相较于封装基本类型变量的结构体多了两个管理内存的函数指针成员变量。
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int value1; NSNumber *number1; __Block_byref_value2_0 *value2; // by ref __Block_byref_number2_1 *number2; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _value1, NSNumber *_number1, __Block_byref_value2_0 *_value2, __Block_byref_number2_1 *_number2, int flags=0) : value1(_value1), number1(_number1), value2(_value2->__forwarding), number2(_number2->__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_value2_0 *value2 = __cself->value2; // bound by ref __Block_byref_number2_1 *number2 = __cself->number2; // bound by ref int value1 = __cself->value1; // bound by copy NSNumber *number1 = __cself->number1; // bound by copy int blockValue = testS.num; int sum = value1 + (value2->__forwarding->value2) + ((int (*)(id, SEL))(void *)objc_msgSend)((id)number1, sel_registerName("intValue")) + ((int (*)(id, SEL))(void *)objc_msgSend)((id)(number2->__forwarding->number2), sel_registerName("intValue")); (value2->__forwarding->value2) = 5; (number2->__forwarding->number2) = ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 6); }
block 执行时,从
__main_block_impl_0
结构体中获取封装有外部变量的结构体。
综上所述,经过 __block
关键字修饰的变量,实际会被封装到一个结构体中,所以变量所保存的真实值由该结构体持有,block 内外对该变量的操作都经由该结构体。
block 中的循环引用
-
准备一个
Person.m
文件,如下#import "Person.h" @interface Person() @property (nonatomic, strong) NSString * name; @property (nonatomic, strong) void (^test)(void); @end @implementation Person - (void)testMethod { self.test = ^{ self.name = @"han"; }; } @end
在该文件中,定义一个 block 类属性,并且在 block 中使用实例对象本身。
-
执行
clang Person.m -rewrite-objc -o person.cpp
命令,查看转化后的代码。static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[5]; } _OBJC_$_INSTANCE_METHODS_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_objc_method), 5, {{(struct objc_selector *)"testMethod", "v16@0:8", (void *)_I_Person_testMethod}, {(struct objc_selector *)"name", "@16@0:8", (void *)_I_Person_name}, {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_Person_setName_}, {(struct objc_selector *)"test", "@?16@0:8", (void *)_I_Person_test}, {(struct objc_selector *)"setTest:", "v24@0:8@?16", (void *)_I_Person_setTest_}} };
生成了一个静态的结构体变量
_OBJC_$_INSTANCE_METHODS_Person
来记录Person
类的实例对象方法。从该结构体中可知,testMethod
方法的具体实现为_I_Person_testMethod
。static void _I_Person_testMethod(Person * self, SEL _cmd) { ((void (*)(id, SEL, void (*)()))(void *)objc_msgSend)((id)self, sel_registerName("setTest:"), ((void (*)())&__Person__testMethod_block_impl_0((void *)__Person__testMethod_block_func_0, &__Person__testMethod_block_desc_0_DATA, self, 570425344))); }
在该静态函数中,只是为
Person
的实例对象属性test
赋值,这个属性是 block ,而objc_msgSend
第三个参数就是 block 变量。如此,便回到了 block 的定义原理上来,接着查看__Person__testMethod_block_impl_0
结构体。struct __Person__testMethod_block_impl_0 { struct __block_impl impl; struct __Person__testMethod_block_desc_0* Desc; Person *self; __Person__testMethod_block_impl_0(void *fp, struct __Person__testMethod_block_desc_0 *desc, Person *_self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
可知,引用
self
同引用其他变量一样,都是在构造该结构体时进行了赋值。static void __Person__testMethod_block_func_0(struct __Person__testMethod_block_impl_0 *__cself) { Person *self = __cself->self; // bound by copy ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)self, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_65_h_h1bqw127sgk8bxd4r5k8200000gn_T_Person_aec206_mi_0); }
在由 block 任务生成的静态函数中,会生成一个局部变量,该变量由结构体中的
self
成员变量赋值。那么这里为什么会造成循环引用呢!
在结构体
__Person__testMethod_block_impl_0
构造时,传递了self
指针对成员变量self
进行赋值,默认是强引用,会增加Person
实例的引用计数。另外,block 又赋值给了Person
的实例对象属性。两者相互持有,那么都无法释放。实际上,如果跟踪 self 的引用计数,会发现,其变化从 1 到 3 最后到 2 ,这是因为在 testMethod 方法中为 self.test 赋值时,
__Person__testMethod_block_impl_0
被创建,这个结构体或者说 block 的有效范围是这个方法,是个栈变量,赋值操作,会使得栈 block 被拷贝到堆中,而捕获的 self 因为是默认 strong 的修饰符,那么在拷贝过程中,会使得其所指向的对象引用计数增 1 ,所以在赋值操作后,self 的引用计数是 3 ,当方法结束,栈 block 被回收,引用计数变为 2 ,这多出来的 1 就是堆 block 对 self 的强引用。使用
__weak
及__strong
进行修饰- (void)testMethod { __weak typeof(self) weakSelf = self; self.test = ^{ __strong typeof(weakSelf) strongSelf = weakSelf; strongSelf.name = @"han"; }; }
如此,在 block 中使用的外部变量是经过
__weak
修饰的weakSelf
变量,所以结构体中的成员变量从self
变成了weakSelf
,并且在构造过程中不会增加实例对象的引用计数值。struct __Person__testMethod_block_impl_0 { struct __block_impl impl; struct __Person__testMethod_block_desc_0* Desc; typeof (self) weakSelf; __Person__testMethod_block_impl_0(void *fp, struct __Person__testMethod_block_desc_0 *desc, typeof (self) _weakSelf, int flags=0) : weakSelf(_weakSelf) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
而在 block 执行时,为了避免在运行过程中实例对象的释放,则需要使用
__strong
字段修饰,获取一个局部变量strongSelf
强引用实例对象,直到 block 执行结束。static void __Person__testMethod_block_func_0(struct __Person__testMethod_block_impl_0 *__cself) { typeof (self) weakSelf = __cself->weakSelf; // bound by copy __attribute__((objc_ownership(strong))) typeof (self) strongSelf = weakSelf; ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)strongSelf, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_65_h_h1bqw127sgk8bxd4r5k8200000gn_T_Person_ee40ea_mi_0); }
这里使用 typeof() 获取变量的类型,同时也会获取其内存引用类型。对于捕获的 weakSelf 而言,就是 __weak ,所以结构体的初始化以及拷贝,都不会导致 self 的引用计数发生变化。