循环引用
循环引用是iOS开发常见的问题,虽然现在普遍是ARC工程,但是这个问题仍然无可避免。一般都是两个强引用对象互相持有对方,导致两个对象都无法被正常销毁,也就是说,引用计数始终无法为0,系统无法回收内存,导致内存泄露。
block循环引用
@property (copy,nonatomic) void(^block)(void);
...
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.block = ^{
NSLog(@"self =%@",self);
};
}
这时候编译器会提示
Capturing 'self' strongly in this block is likely to lead to a retain cycle
这就是典型的block循环引用问题。
因为self
持有block
,而block
捕获self
变量,也持有self
。也就是互相持有,导致这个ViewController的不能销毁释放,就是dealloc
无法被调用。
注意,就算此时block没有执行,只要定义并且创建了这个block,block就会捕获外部变量self,就会导致了retain cycle问题。
weak-strong dance
解决这个问题,苹果官方推荐了一种方法,就是使用以下方法
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
__weak typeof(self) w = self;
self.block = ^{
typeof(self) s = w;
NSLog(@"self =%@",s);
};
}
原理简单来说就是在block外面把self强行转换为weak类型,然后block执行的时候,又强行转换为strong。如果是单纯地解决retain cycle问题的话,在block声明__weak typeof(self) w = self;
即可,那为什么还需要在block里面又强引用一次self呢?typeof(self) s = w;
,这样不是导致self又无法释放销毁了吗?
因为s
是一个局部变量,生命周期只是在block执行期间,一旦block执行完毕,里面强引用的s
就会被释放销毁,self自然就销毁了。这么做的主要用途是防止在执行block期间
,self被释放销毁了。万一block执行代码有需要用到self的话,self被置为nil,就执行不了了。
这里需要明确,这是在block执行期间,typeof(self) s = w;
这句代码执行前,self一定要保证不能被销毁,如果被销毁置为nil,就算使用这句代码,s
一样是为nil的。
@interface SecondViewController ()
@property (copy,nonatomic) void(^block)(void);
@end
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
__weak typeof(self) w = self;
self.block = ^{
typeof(self) s = w;
NSLog(@"s0 =%@",s);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"s1 =%@",s);
});
};
self.block();
NSLog(@"self.block()");
}
- (void)dealloc {
NSLog(@"seconde deallo");
}
Log输出结果如下
2018-04-09 11:05:35.987263+0800 TestMRC[24989:4591265] s0 =<SecondViewController: 0x7fe295715680>
2018-04-09 11:05:35.987412+0800 TestMRC[24989:4591265] self.block()
2018-04-09 11:05:45.987931+0800 TestMRC[24989:4591265] s1 =<SecondViewController: 0x7fe295715680>
2018-04-09 11:05:45.988073+0800 TestMRC[24989:4591265] seconde deallo
如果想使得s
有效,务必保证block要在self释放销毁之前操作,但是一般编程,这个不可控,所以还是得在block里面判断self是否为nil。
不是所有block都要加weak-strong dance
有些人,看到block都要加weak-strong dance代码,虽然没啥问题,但是也可不必加的。简单来说,一般block与self造成了循环引用才需要加。
例如以下
这些block是局部变量,随着作用域的结束,block的生命周期也会结束,不存在循环引用问题。
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"s =%@",self);
});
TestBlock *testBlock = [TestBlock new];
testBlock.block = ^{
NSLog(@"s =%@",self);
};
所有权修饰符
-
__strong修饰符
对象的默认修饰符,强引用 -
__weak修饰符
弱引用 -
__unsafe_unretained修饰符
__unsafe_unretained修饰符是不安全的修饰符,尽管ARC式的内存管理是编译器的工作,但附有__unsafe_unretained修饰符的变量不属于编译器的内存管理对象。__unsafe_unretained和__weak一样不能持有对象。 -
__autoreleasing修饰符
网友评论