ReactiveCocoa中的内存管理

时间:2014-07-13 07:57:38

标签: ios objective-c objective-c-blocks reactive-cocoa

我刚读了tutorial about ReactiveCocoa

在"避免保留周期"作者说,为了避免保留周期,我们应该在self块中用bself替换subscribeNext。但是他将self保留在map区块中。

__weak RWSearchFormViewController *bself = self; // Capture the weak reference
[[self.searchText.rac_textSignal
  map:^id(NSString *text) {
    return [self isValidSearchText:text] ?
      [UIColor whiteColor] : [UIColor yellowColor];
  }]
  subscribeNext:^(UIColor *color) {
    bself.searchText.backgroundColor = color;
  }];

这种做法对吗?为什么呢?

2 个答案:

答案 0 :(得分:26)

我们可以用科学回答!

虽然我赞扬该教程提到保留周期,但我认为它没有给它一个非常好的处理(更不用说它是错误的)。当我们需要@weakify时,计算它实际上是一件非常容易的事情(虽然在写这篇文章的过程中我意识到要清楚地解释它是一件困难的事情)。

让我们再看一下代码:

__weak typeof(self) weakSelf = self;
[[self.searchText.rac_textSignal map:^id(NSString *text) {
    return [weakSelf isValidSearchText:text] ? [UIColor whiteColor] : [UIColor yellowColor];
}] subscribeNext:^(UIColor *color) {
    weakSelf.searchText.backgroundColor = color;
}];

要回答“这里是否存在保留周期”,我们需要做的就是找出何时将解除分配。如果答案是“从不”,我们知道我们有问题。

所以让我们从最顶层开始:信号的根是rac_textSignal。当其后备textViewtextField被解除分配时(我不知道哪个searchText),此信号将完成(并且,除非我们明确地将其保留在其他位置,否则会解除分配)。我们可以查看实现并看到:

return [[[[[RACSignal defer:...
] concat:...
] map:...
] takeUntil:self.rac_willDeallocSignal // <-- bingo (self is the text field here)
] setNameWithFormat:...];

因此,只要UITextField不再保留,“底层”信号就会完成。除非我们添加taketakeUntil或其他内容来改变这一事实,否则整个信号链将一旦完成(并且模糊不清,将被解除分配)根信号确实。

所以现在我们要问UITextField何时被解除分配。

如果self对其有强烈的引用,则UITextFieldself发布之前不会解除分配(可能在[self dealloc]中)。 (实际上,文本字段也可能在视图层次结构中,因此它需要在它实际消失之前出现,但让我们假设在self被释放之前发生这种情况(如果我们的视图控制器应该这样做)代码很好)只是假装self是唯一保留它的东西。)

如果self对文本字段有引用,则我们不知道,需要收集更多信息以确定保留的内容它还活着。在这种情况下,强烈引用块中的self可能会间接导致保留周期,但可能不会。

让我们假设它很强大。现在分析非常简单:文本信号在文本字段消失之前不会消失。在self消失之前,文本字段不会消失。 self直到......才会消失。

从不。因为信号所保留的块保留self

textField -> textSignal -> mapped signal
    ^                           |
    |                           |
    +-- self   <---   block  <--+

骑自行车镇。这里的箭头模糊地表示“保持活着”,而不是“保留”,因为根文本信号不直接保留映射信号,但映射信号的生命周期仍然与文本信号的生命周期绑定。

因此,使用此分析,我们可以得出结论,我们希望在这两个块中弱引用self

但是用这些术语来思考它可以帮助我们以更通用的方式弄清楚这些弱引用是否必要 - 如果你只是说“当你订阅时弱自我引用”作为一个经验法则(如本教程的作者),你会犯这样的错误。


现在让我们试试硬模式:

UITextField *textField = self.searchText;
__weak typeof(self) weakSelf = self;
[[self.searchText.rac_textSignal map:^id(NSString *text) {
    return [weakSelf isValidSearchText:text] ? [UIColor whiteColor] : [UIColor yellowColor];
}] subscribeNext:^(UIColor *color) {
    textField.backgroundColor = color;
}];

我们好吗?当然不是!强烈引用块中的文本字段同样糟糕,这与保持周期一样多。这就是我们现在拥有的:

textField -> textSignal -> mapped signal
  ^  ^                          |
  |  |                          |
  |  +-------------  block  <---+
  |
 self

如果我们在心态中“只有self会导致保留周期”(一般来说阻止保留周期博客教程编写者的观点,不仅仅是在ReactiveCocoa世界中)我们会从不考虑检查这样的事情。但是如果我们花一分钟思考信号的生命周期,那么理解为什么self并不是特别的,这并不是太棘手。

libextobjcEXTScope在这种情况下真的很闪耀。它不是必须声明另一个“弱文本字段”变量,而是允许我们在需要时将其添加到@weakify列表和@strongify中:

UITextField *textField = self.searchText;
@weakify(self, textField);
[[self.searchText.rac_textSignal map:^id(NSString *text) {
    @strongify(self);
    return [self isValidSearchText:text] ? [UIColor whiteColor] : [UIColor yellowColor];
}] subscribeNext:^(UIColor *color) {
    @strongify(textField);
    textField.backgroundColor = color;
}];

答案 1 :(得分:0)

是的,避免块中的保留周期是一种好习惯。所以取代自我。这很好。