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拷贝函数__main_block_copy_0
和Block销毁函数__main_block_dispose_0
,这两个函数会保存在__main_block_desc_0
结构体的copy
和dispose
成员中;另外添加了一个__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 *)©->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++代码的结构体关系图:
从类图上可以明显的看到__YTObject__testReferenceSelf_block_impl_0
和YTObject
之间有循环依赖的关系,这样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_0
对YTObject
的依赖是弱依赖。weak修饰变量是在runtime中进行处理的,在YTObject
对象的Dealloc方法中会调用weak引用的处理方法,从weak_table
中寻找弱引用的依赖对象,进行清除处理,可以查看Runtime源码中objc_object::clearDeallocating
该方法的处理逻辑,另外关于__weak
修饰的变量的详细处理可以查看Runtime相关的知识
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底层实现的一些分析,不妥之处敬请指教