关于 weakSelf 和 strongSelf

block 带来了一定程度的便利性,在作为回调使用时,时常会遇到内存问题,即「引用循环」,这在 Objc 中是一个绕不开的话题。

试着看如下场景:

1
2
3
4
5
6
7
8
9
10
DemoViewController.m

- (void)viewDidLoad {
[super viewDidLoad];
CustomView *v = [[CustomView alloc] init];
[self.view addSubview:v];
v.callbackBlock = ^{
[self doSomething];
}
}

viewDidLoad 方法执行时,创建一个 block 并赋值给对象 vcallbackBlock 属性,callbackBlock 捕捉 selfself 持有 self.view, v 在 addSubview 后成为 self.view 的子 view 而被 self.view 持有,这样就形成了一个引用循环,如下:

而如果 CustomView 对象不被创建时,不会造成引用循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
DemoViewController.m

- (void)viewDidLoad {
[super viewDidLoad];
}

- (void)setup {
CustomView *v = [[CustomView alloc] init];
[self.view addSubview:v];
v.callbackBlock = ^{
[self doSomething];
}
}

我们可以打破这个循环

1
2
3
4
5
6
7
8
9
10
11
DemoViewController.m

- (void)viewDidLoad {
[super viewDidLoad];
CustomView *v = [[CustomView alloc] init];
[self.view addSubview:v];
__weak __typeof__(self) weakSelf = self;
v.callbackBlock = ^{
[weakSelf doSomething];
}
}

callbackBlock 不直接引用 self,影响 self 的释放,由此打破循环,所有对象可以正常释放。

而当下面情况出现时,行为开始变得奇怪起来:

1
2
3
4
5
6
7
8
9
10
11
12
DemoViewController.m

- (void)viewDidLoad {
[super viewDidLoad];
CustomView *v = [[CustomView alloc] init];
[self.view addSubview:v];
__weak __typeof__(self) weakSelf = self;
v.callbackBlock = ^{
[weakSelf doSomething];
[weakSelf doAnoterThing];
}
}

callbackBlock 开始执行时,DemoViewController 对象已经释放,callbackBlockweakSelf 始终为 nil,不会造成什么影响,若在 callbackBlock 开始执行时 DemoViewController 对象还存在,在 doSomething 时,weakSelf 一定不为 nil,而在 doAnoterThing 时就不一定了,我项目中出现复杂的场景时会造成 callbackBlock 执行行为不一致,导致一些奇怪的问题,这个时候我可以利用引用计数的特性来解决,方案变成了下面:

1
2
3
4
5
6
7
8
9
10
11
12
13
DemoViewController.m

- (void)viewDidLoad {
[super viewDidLoad];
CustomView *v = [[CustomView alloc] init];
[self.view addSubview:v];
__weak __typeof__(self) weakSelf = self;
v.callbackBlock = ^{
__typeof__(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongSelf doAnoterThing];
}
}

callbackBlock 开始执行时,产生一个对 weakSelf 的强引用,这样在 callbackBlock 的作用域范围内,self 不可能被释放,这样 callbackBlock 的执行行为在所有情况下是一致的,避免产生一些奇怪的问题。

那么如果 block 出现嵌套呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
DemoViewController.m

- (void)viewDidLoad {
[super viewDidLoad];
CustomView *v = [[CustomView alloc] init];
[self.view addSubview:v];
__weak __typeof__(self) weakSelf = self;
v.callbackBlock = ^{
__typeof__(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongSelf doAnoterThing];
obj.objCallbackBlock = ^{
[strongSelf doObjSomething];
[strongSelf doObjAnotherThing];
}
}
}

objCallbackBlock 捕捉的是 strongSelf,而 strongSelf 实际上还是指向对 self 的弱引用,strongSelf 可以保证在 callbackBlock 执行期间不释放,但是在 objCallbackBlock 执行期间并不能保证一定存在,所以还是会有执行不一致问题,所以在 objCallbackBlock 中再次对 weakSelf 进行一次强引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
DemoViewController.m

- (void)viewDidLoad {
[super viewDidLoad];
CustomView *v = [[CustomView alloc] init];
[self.view addSubview:v];
__weak __typeof__(self) weakSelf = self;
v.callbackBlock = ^{
__typeof__(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongSelf doAnoterThing];
obj.objCallbackBlock = ^{
__typeof__(self) strongSelf = weakSelf;
[strongSelf doObjSomething];
[strongSelf doObjAnotherThing];
}
}
}

以保证所有 block 的执行行为一致。

参考资料