循环引用示例
- block会对block中的对象进行持有操作,如果block中的对象又持有了该block就会造成循环引用
- 典型的场景就是block作为self的属性使用时,又在block调用了self的属性或方法,代码如下
// Test.h
#import <Foundation/Foundation.h>
typedef void (^MyBlock)();
@interface Test
@property(nonatomic,copy) MyBlock blockTest;
@property(nonatomic,copy) NSString* name;
-(void)print;
@end
// Test.m
#import "Test.h"
@implementation Test
-(void)print{
self.blockTest = ^{
NSLog(@"%@",self.name);
};
self.blockTest();
}
@end
- 上例中,self和blockTest相互持有了对方,self的销毁依赖于blockTest的销毁,blockTest的销毁依赖于self的销毁,造成了循环引用
解决
- 在ARC下,使用
__weak
修饰符定义一个self
的弱引用,并在block中使用弱引用可以解决循环引用问题,代码如下
-(void)print{
__weak typedof(self) weakSelf = self;
self.blockTest = ^{
NSLog(@"%@",weakSelf.name);
};
self.blockTest();
}
- 但block的执行时间不确定,当block被执行的时候被
__weak
修饰的self
对象可能已经被释放了,比如ViewController已经被pop了,在涉及异步和并发时这种情况有可能发生
- 对于这种情况,应在block中使用
__strong
修饰符修饰self
,这样在block执行期间持有对象,block执行结束后解除持有,代码如下:
-(void)print{
__weak typedef(self) weakSelf = self;
self.blockTest = ^{
__strong typeof(self) strongSelf = weakSelf
NSLog(@"%@",strongSelf.name);
};
self.blockTest();
}
block中使用self一定会引起循环引用?
- 不一定,比如大多的GCD方法
- 只有当block和self相互持有时才会导致循环引用
/*将block异步派发到主队列*/
dispatch_async(dispatch_get_main_queue(),^{
[self doSomething];
});
上面self没有对GCD中的block进行持有,只是block持有了self的引用,因此不会造成循环引用