导语:使用weakSelf,strongSelf可以解决循环引用原理分析
在使用block过程中经常会出现循环引用的情况:
比如下面的代码:
self.block = ^() {
[self doSomething];
};
因为block作为当前对象的一个属性,又强引用了该对象,导致循环引用。
解决方法一般:
__weak __typeof(self) weakSelf = self;
self.block = ^() {
__strong __typeof(weakSelf) strongSelf = weakSelf;
[strongSelf doSomething];
};
但是该解决方式在block中又强引用了weakSelf,为什么没有导致循环引用呢?
之前也一直比较疑惑,后来分析了下block的源码,终于搞清楚了。
首先,我们看一下什么是block。
(1)新建一个文件,定义一个block并执行:
#include <stdio.h>
int main(){
void (^blk)(void) = ^(){printf("Hello world!");};
blk();
return 0;
}
对于这个简单的block文件使用 clang -rewrite-objc hello.c 语句可以得到它对应的cpp文件,主要代码有:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
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) {
printf("Hello world!");}
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(){
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
首先__block_impl结构体中有四个变量:其中isa表明block也有runntime的特性,isa是指向Class的指针;FuncPtr是函数指针,指向block实际执行的函数。
__main_block_impl_0结构体有两个变量:一个是__block_impl变量;一个是__main_block_desc_0变量,是对__main_block_impl_0的描述。
__main_block_func_0是一个方法,也就是调用block时执行的函数。
再看main()函数,可以看到block实际被转换成一个指向__main_block_impl_0结构体的指针,block()执行过程也就是blk->FuncPtr()的执行过程。
(2)修改一下文件中的代码:
#include <stdio.h>
int main(){
int i = 0;
void (^blk)(void) = ^(){printf("i is %d", i);};
i++;
blk();
return 0;
}
转换成cpp文件:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int i;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int i = __cself->i; // bound by copy
printf("i is %d", i);}
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 i = 0;
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i));
i++;
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
和之前的代码比较可以发现,在__main_block_impl_0结构体中多了一个变量i,并且在创建block的时候i被赋值,注意int i = __cself->i; // bound by copy 所以block创建之后再修改i的值对于block的执行结果没有影响。
(3)继续修改:
#include <stdio.h>
int main(){
int __block i = 0;
void (^blk)(void) = ^(){printf("i is %d", i);};
i++;
blk();
return 0;
}
cpp文件:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __Block_byref_i_0 {
void *__isa;
__Block_byref_i_0 *__forwarding;
int __flags;
int __size;
int i;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_i_0 *i; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_i_0 *i = __cself->i; // bound by ref
printf("i is %d", (i->__forwarding->i));}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->i, 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};
int main(){
__Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 0};
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));
(i.__forwarding->i)++;
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
该block中包含可以修改的变量,情况相对比较复杂。只看变化比较明显的地方,此时i是通过__Block_byref_i_0 *i; // by ref 引用到外面的i值,也就是外面i值的改变是可以改变block中的i值。更深入的分析由于水平有限暂时不展开了。
所以:
self.block = ^() {
[self doSomething];
};
网友评论