之所有要写这篇weak-strong-dance,是因为一个同事在聊天时表达了对这种写法困惑,在block中为什么要重新使用strong来修饰觉得没有必要,所以我写下这篇文章一来可以为他解惑,二来如果能帮助更多的同学理解weak-strong-dance就再好不过了

我们在使用RAC的时候经常会用到两个宏:@weakify(self)和@strongify(self),这两个宏等价于 weak typeof(self) weakSelf = self; 和 strong typeof(self) strongSelf = weakSelf;,其实就是使用weak 和 strong来修饰当前的self对象。rac中用到了大量的block,在block中使用weak我们知道是为了防止循环引用,那么重新修饰成strong类型有什么作用呢?其实这里的weak和strong有一个美丽的名字:weak-strong dance

之前我们已经介绍了,在block中调用外部属性,为了避免循环引用,我们需要使用weak来修饰对象

1
2
3
4
5
6
7
8
9
10
11
@interface ViewController ()
@property (nonatomic, copy) NSString *name;
@end

- (void)blockTest{
__weak typeof (self)weakSelf = self;
void(^test)(void) = ^{
NSLog(@"%@", weakSelf.name);
};
test();
}

在这里我们使用weak修饰了self,从而避免了循环引用的问题,但是使用weak是不是就能解决所有的问题呢?我们来看一下这个场景:

  1. 有两个ViewController, A 和 B
  2. 在A中点击按钮跳转到B
  3. B中设置两个定时任务
    1)定时任务1:3s的时候执行dismissViewController方法
    2)定时任务2:在任务1中设置6s后打印一个属性变量name
    ok,需求很简单,我们来看一下B VC的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@interface BViewController ()
@property (nonatomic, copy) NSString *name;
@end

- (void)viewDidLoad{
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
self.name = @"kate";
__weak typeof(self) weakSelf = self;
void (^test)(void) = ^{

[NSTimer scheduledTimerWithTimeInterval:3 repeats:NO block:^(NSTimer * _Nonnull timer) {
NSLog(@"3s后");
[weakSelf dismissViewControllerAnimated:YES completion:nil];
[NSTimer scheduledTimerWithTimeInterval:6 repeats:NO block:^(NSTimer * _Nonnull timer) {
NSLog(@"6s后");
NSLog(@"%@", weakSelf.name);
}];
}];
};
test();
}

- (void)dealloc{
NSLog(@"dealloc");
}

这段代码的执行结果是怎样的呢,大家可以先思考一下

运行结果:

1
2
3
4
2019-03-12 14:49:09.585089+0800 Block[3543:669240] 3s后
2019-03-12 14:49:10.091926+0800 Block[3543:669240] dealloc
2019-03-12 14:49:15.587989+0800 Block[3543:669240] 6s后
2019-03-12 14:49:15.588211+0800 Block[3543:669240] (null)

可以看到,因为3s后B视图被销毁,使用weak修饰的self变成了nil,name自然就无法打印出来。

在这种情况下,我们使用上文中提到的weak-strong dance就可以完美解决:

B视图修改后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)viewDidLoad{
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
self.name = @"kate";
__weak typeof(self) weakSelf = self;
void (^test)(void) = ^{

[NSTimer scheduledTimerWithTimeInterval:3 repeats:NO block:^(NSTimer * _Nonnull timer) {
__strong typeof(self) strongSelf = weakSelf;
NSLog(@"3s后");
[strongSelf dismissViewControllerAnimated:YES completion:nil];
[NSTimer scheduledTimerWithTimeInterval:6 repeats:NO block:^(NSTimer * _Nonnull timer) {
NSLog(@"6s后");
NSLog(@"%@", strongSelf.name);
}];
}];
};
test();
}

我们只是在timer内容使用strong重新修饰了weakSelf,然后使用strongSelf调用了相应的方法和属性
执行结果:

1
2
3
4
2019-03-12 14:56:37.647339+0800 Block[3648:695087] 3s后
2019-03-12 14:56:43.649361+0800 Block[3648:695087] 6s后
2019-03-12 14:56:43.649604+0800 Block[3648:695087] kate
2019-03-12 14:56:43.649789+0800 Block[3648:695087] dealloc

我们可以看到 name属性可以被打印出来了

看到这里,大家应该都认识了weak-storng dance,其实weak-storng dance主要解决的就是当block执行了一半时候被销毁,导致block内部出现的一些异常问题,比如视图被销毁,block在多线程中被销毁等情况,所以为了我们的代码更加健壮,请尽量使用weak-storng dance把。