我们都知道block如果直接或者间接的使用了self,同时self又直接或间接的强持有block的话,那么就出现了循环引用的问题;出现这种情况我们往往就是使用weak来修饰一个弱引用来打破这个循环解决循环引用的问题
下面从原理来解释weak是如何解除循环引用的;这里我们可以使用clang rewrite来查看rewrite之后的代码来加深认识
模拟器:
xcrun -sdk iphonesimulator clang -rewrite-objc -fobjc-arc -fblocks -mios-version-min=8.0.0 -fobjc-runtime=ios-8.0.0
真机:
xcrun -sdk iphoneos clang -rewrite-objc -fobjc-arc -fblocks -mios-version-min=8.0.0 -fobjc-runtime=ios-8.0.0
使用alias设置别名,避免每次都打一长串指令:
终端输入:alias rewrite-oc=‘xxxx.m’
小技巧:关闭终端,别名会失效,避免每次都设置,我们可以将设置别名添加到配置文件.bashrc中,设置好了在终端 source .bashrc即可;然后在终端的设置中可以在终端启动执行指令
循环引用是如何产生的
测试代码:
- (void)testBlockRetainCycle {
self.testRetainCycleBlock = ^(void){
NSLog(@"%@", self);
};
}
clang rewrite之后查看转化之后的关键代码段:
struct __BlockTest__testBlockRetainCycle_block_impl_0 {
struct __block_impl impl;
struct __BlockTest__testBlockRetainCycle_block_desc_0* Desc;
BlockTest *const __strong self;
__BlockTest__testBlockRetainCycle_block_impl_0(void *fp, struct __BlockTest__testBlockRetainCycle_block_desc_0 *desc, BlockTest *const __strong _self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __BlockTest__testBlockRetainCycle_block_func_0(struct __BlockTest__testBlockRetainCycle_block_impl_0 *__cself) {
BlockTest *const __strong self = __cself->self; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_8j_551wkz1d1rg878kt8mg21tg40000gn_T_BlockTest_e1e0de_mi_15, self);
}
static void __BlockTest__testBlockRetainCycle_block_copy_0(struct __BlockTest__testBlockRetainCycle_block_impl_0*dst, struct __BlockTest__testBlockRetainCycle_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __BlockTest__testBlockRetainCycle_block_dispose_0(struct __BlockTest__testBlockRetainCycle_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static struct __BlockTest__testBlockRetainCycle_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __BlockTest__testBlockRetainCycle_block_impl_0*, struct __BlockTest__testBlockRetainCycle_block_impl_0*);
void (*dispose)(struct __BlockTest__testBlockRetainCycle_block_impl_0*);
} __BlockTest__testBlockRetainCycle_block_desc_0_DATA = { 0, sizeof(struct __BlockTest__testBlockRetainCycle_block_impl_0), __BlockTest__testBlockRetainCycle_block_copy_0, __BlockTest__testBlockRetainCycle_block_dispose_0};
static void _I_BlockTest_testBlockRetainCycle(BlockTest * self, SEL _cmd) {
((void (*)(id, SEL, void (*)()))(void *)objc_msgSend)((id)self, sel_registerName("setTestRetainCycleBlock:"), ((void (*)())&__BlockTest__testBlockRetainCycle_block_impl_0((void *)__BlockTest__testBlockRetainCycle_block_func_0, &__BlockTest__testBlockRetainCycle_block_desc_0_DATA, self, 570425344)));
}
可以看到
-
__BlockTest__testBlockRetainCycle_block_impl_0
结构体内部有一个strong修饰的selfBlockTest *const __strong self;
- 在结构体初始化的时候传递的是self对象
- block在arc模式下strong/copy修饰会从栈拷贝到堆上,在block释放的时候执行dispose释放self
由于self强持有block,block内部又强持有self;那么就形成了环,block等self dealloc才释放,self又由于block内部的强持有导致无法dealloc,就产生了循环引用
如何解决循环引用
想要解决循环引用,就是要断开这个相互强持有的关系;你可以在合适的时机将block置为nil也可以打破这种环,当然最好的方式还是使用weak的方式
测试代码:
- (void)testWeakBreakBlockRetainCycle {
__weak typeof(self) weakSelf = self;
self.testRetainCycleBlock = ^(void){
NSLog(@"%@", weakSelf);
};
}
clang rewrite之后查看转化之后的关键代码段:
struct __BlockTest__testWeakBreakBlockRetainCycle_block_impl_0 {
struct __block_impl impl;
struct __BlockTest__testWeakBreakBlockRetainCycle_block_desc_0* Desc;
BlockTest *const __weak weakSelf;
__BlockTest__testWeakBreakBlockRetainCycle_block_impl_0(void *fp, struct __BlockTest__testWeakBreakBlockRetainCycle_block_desc_0 *desc, BlockTest *const __weak _weakSelf, int flags=0) : weakSelf(_weakSelf) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __BlockTest__testWeakBreakBlockRetainCycle_block_func_0(struct __BlockTest__testWeakBreakBlockRetainCycle_block_impl_0 *__cself) {
BlockTest *const __weak weakSelf = __cself->weakSelf; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_8j_551wkz1d1rg878kt8mg21tg40000gn_T_BlockTest_e57b64_mi_18, weakSelf);
}
static void __BlockTest__testWeakBreakBlockRetainCycle_block_copy_0(struct __BlockTest__testWeakBreakBlockRetainCycle_block_impl_0*dst, struct __BlockTest__testWeakBreakBlockRetainCycle_block_impl_0*src) {_Block_object_assign((void*)&dst->weakSelf, (void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __BlockTest__testWeakBreakBlockRetainCycle_block_dispose_0(struct __BlockTest__testWeakBreakBlockRetainCycle_block_impl_0*src) {_Block_object_dispose((void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static struct __BlockTest__testWeakBreakBlockRetainCycle_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __BlockTest__testWeakBreakBlockRetainCycle_block_impl_0*, struct __BlockTest__testWeakBreakBlockRetainCycle_block_impl_0*);
void (*dispose)(struct __BlockTest__testWeakBreakBlockRetainCycle_block_impl_0*);
} __BlockTest__testWeakBreakBlockRetainCycle_block_desc_0_DATA = { 0, sizeof(struct __BlockTest__testWeakBreakBlockRetainCycle_block_impl_0), __BlockTest__testWeakBreakBlockRetainCycle_block_copy_0, __BlockTest__testWeakBreakBlockRetainCycle_block_dispose_0};
static void _I_BlockTest_testWeakBreakBlockRetainCycle(BlockTest * self, SEL _cmd) {
__attribute__((objc_ownership(weak))) typeof(self) weakSelf = self;
((void (*)(id, SEL, void (*)()))(void *)objc_msgSend)((id)self, sel_registerName("setTestRetainCycleBlock:"), ((void (*)())&__BlockTest__testWeakBreakBlockRetainCycle_block_impl_0((void *)__BlockTest__testWeakBreakBlockRetainCycle_block_func_0, &__BlockTest__testWeakBreakBlockRetainCycle_block_desc_0_DATA, weakSelf, 570425344)));
}
可以看到
- block内部的结构体
BlockTest *const __weak weakSelf;
使用的是weak修饰的
由于block内部的对于self的引用变成了weak修饰的,那么就打破了这个环,解决了循环引用