block 中的循环引用问题

block 的使用,为程序的开发带来了很大的方便,但是与此同时,block 带来的循环引用问题也是要注意的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@interface MyBlockClass : NSObject

@property (nonatomic, copy) void (^block)();

@end

@implementation MyBlockClass

-(void)methodBlock{
self.block = ^{
[self methodOne];
};
}

@end

上面的代码生命了一个 block 属性,使得 self 对 block 有一个强引用。在 block 内又对 self 进行了强引用,形成一个闭环,即循环引用。这样内存不能被释放,就会造成内存泄漏。解决这样的循环引用通常是声明一个 weakSelf,更改后的代码:

1
2
3
4
5
6
-(void)methodBlock{
__weak typeof(self) weakSelf = self;
self.block = ^{
[weakSelf methodOne];
};
}

这样 block 对 self 的引用就是一个弱引用关系,这样就打破了循环引用的闭环。

虽然解决了循环引用的问题,但是还有一个问题值得注意:

1
2
3
4
5
6
7
-(void)methodBlock{
__weak typeof(self) weakSelf = self;
self.block = ^{
[weakSelf methodOne];
[weakSelf methodTwo];
};
}

在并发执行的时候 block 是可以被抢占的,对 weakSelf 指针的调用时序不同也会导致不同的结果,在特定的时序下 weakSelf 可能会变成 nil。为避免这个问题,采用 __strong 的方式进行避免,更改后的代码如下:

1
2
3
4
5
6
7
8
-(void)methodBlock{
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
[weakSelf methodOne];
[weakSelf methodTwo];
};
}

这样就算是 block 被抢占,strongSelf 还是非 nil 的。
weakself 是为了 block 不持有 self,避免循环引用;strongSelf 是因为一旦进入 block 执行,就不允许 self 在这个执行过程中释放。block 执行完成,这个 strongSelf 会自动释放,没有循环引用问题。