如:
//.h文件在ARC
typedef void(^block)(void);
@interface Test : NSObject
@property (nonatomic,copy)block testBlock;
@property (nonatomic,assign)NSInteger temp;
@property (nonatomic,copy)NSString* str;
- (void)inmethod1;
@end
//mian文件在MRC,为了能手动release观察是否发生循环引用。
int main(int argc, char * argv[]) {
Test *test = [[Test alloc]init];
[test inmethod1];
[test release];
NSLog(@"程序即将结束");
}
1、在ARC中(.m文件在ARC中来即测试在ARC中block的影响而导致的循环引用)
分析 :可以看到没有调用dealloc,说明出现了循环引用,那么循环引用怎么产生的呢。
分析 :可以看到没有调用dealloc,说明出现了循环引用,那么循环引用怎么产生的呢。
2、在MRC中(.m文件在MRC中来即测试在MRC中block的影响而导致的循环引用)
分析 :可以看到没有调用dealloc,说明出现了循环引用,那么循环引用怎么产生的呢。
分析 :可以看到调用dealloc了,说明没有出现了循环引用,那是为什么呢。
查看cpp代码
typedef void(*block)(void);
struct Test_IMPL {
struct NSObject_IMPL NSObject_IVARS;
block _testBlock; //生成__testBlock实例变量
NSInteger _temp; //生成_temp实例变量
NSString *_str; //生成实例变量
};
重要部分代码:
struct __Test__inmethod1_block_impl_0 {
......
Test *self; //注意捕获进来了
__Test__inmethod1_block_impl_0(void *fp, struct __Test__inmethod1_block_desc_0 *desc, Test *_self, int flags=0) : self(_self) {
......
}
};
//代码块初始化时候需要填充的结构体struct __Test__inmethod1_block_impl_0
struct __Test__inmethod1_block_impl_0 {
......
Test *self;
__Test__inmethod1_block_impl_0(void *fp, struct __Test__inmethod1_block_desc_0 *desc, int flags=0) {
......
}
};
解决此类循环引用的方法:
方法 1:
ARC中
- (void)inmethod1
{
__weak typeof(self) weakSelf = self; //可以看到这里多了这句
//用__weak修饰归零弱引用self,使得后面即使成环了,但是有一个弱引用,使得循环引用无法发生
//或者用__unsafe_unretain
self.testBlock = ^(void) //调用setter方法对实例变量进行操作
{
_temp = 1; //这里直接访问self中的实例变量,可能会有循环引用的
_str = @"one"; //这里直接访问self中的实例变量,可能会有循环引用的
weakSelf.temp = 1;
//这里没有会发生循环引用的警告了
weakSelf.str = @"one";
//这里没有会发生循环引用的警告了
NSLog(@"%ld",_temp);还是生成了Test *self
//Capturing 'self' strongly in this block is likely to lead to a retain cycle
NSLog(@"%@",_str);还是生成了Test *self
//Capturing 'self' strongly in this block is likely to lead to a retain cycle
//疑问 2:why?因为弱引用weakself没有被调用,而是依然调用的强引用self
NSLog(@"%ld",weakSelf.temp);
//这里没有会发生循环引用的警告了
NSLog(@"%@",weakSelf.str);
//这里没有会发生循环引用的警告了
};
_testBlock();
}
查看转换成的cpp代码
//inmethod1
struct __Test__inmethod1_block_impl_0 {
......
typeof (self) weakSelf; //可以看到在block里是相当使用了弱引用的
__Test__inmethod1_block_impl_0(void *fp, struct __Test__inmethod1_block_desc_0 *desc, typeof (self) _weakSelf, int flags=0) : weakSelf(_weakSelf) {
......
}
};
//block的内部实现函数
static void __Test__inmethod1_block_func_0(struct __Test__inmethod1_block_impl_0 *__cself) {
//这里和上面一样相当使用了弱引用
typeof (self) weakSelf = __cself->weakSelf; // bound by copy
......
}
//inmthod内部实现
static void _I_Test_inmethod1(Test * self, SEL _cmd) {
__attribute__((objc_ownership(none))) typeof(self) weakSelf = self;
......
}
结论:可以看到即使使用__weak或者__unsafe_unretained将self弱引用后,如果在block内部调用的不是self的弱引用,或者_temp、_str这样调用依然会引起循环引用的。
方法 2:方法 1是有缺陷的,所以在方法 1 的基础上加以完善。
//ARC
- (void)inmethod1
{
__weak typeof(self) weakSelf = self; //可以看到这里多了这句
//用__weak修饰归零弱引用self,使得后面即使成环了,但是有一个弱引用,使得循环引用无法发生
//或者用__unsafe_unretain
self.testBlock = ^(void) //调用setter方法对实例变量进行操作
{
_temp = 1; //这里直接访问self中的实例变量,可能会有循环引用的
_str = @"one"; //这里直接访问self中的实例变量,可能会有循环引用的
/* --------------------------------------------- */
__strong typeof(self) strongSelf = weakSelf;
//这里多了这一句,那么疑问 1,这里这个strongSelf有什么作用呢?
/* --------------------------------------------- */
strongSelf.temp = 1;
//这里没有会发生循环引用的警告了
strongSelf.str = @"one";
//这里没有会发生循环引用的警告了
//NSLog(@"%ld",_temp);还是生成了Test *self
Capturing 'self' strongly in this block is likely to lead to a retain cycle
//NSLog(@"%@",_str);还是生成了Test *self
Capturing 'self' strongly in this block is likely to lead to a retain cycle
NSLog(@"%ld",strongSelf.temp);
//这里没有会发生循环引用的警告了
NSLog(@"%@",strongSelf.str);
//这里没有会发生循环引用的警告了
};
_testBlock();
}
方法 3:比方法 2更好
//ARC
- (void)inmethod1
{
@weakify(self) //和方法2那的作用类似
self.testBlock = ^(void) //调用setter方法对实例变量进行操作
{
_temp = 1; //这里直接访问self中的实例变量,可能会有循环引用的
_str = @"one"; //这里直接访问self中的实例变量,可能会有循环引用的
@strongify(self) //多了这一句, //和方法2那的作用类似
strongSelf.temp = 1;
//这里没有会发生循环引用的警告了
strongSelf.str = @"one";
//这里没有会发生循环引用的警告了
//NSLog(@"%ld",_temp);还是生成了Test *self
Capturing 'self' strongly in this block is likely to lead to a retain cycle
//NSLog(@"%@",_str);还是生成了Test *self
Capturing 'self' strongly in this block is likely to lead to a retain cycle
NSLog(@"%ld",strongSelf.temp);
//这里没有会发生循环引用的警告了
NSLog(@"%@",strongSelf.str);
//这里没有会发生循环引用的警告了
};
_testBlock();
}