【问题标题】:Memory management in ReactiveCocoaReactiveCocoa 中的内存管理
【发布时间】:2014-07-13 07:57:37
【问题描述】:

我刚刚阅读了tutorial about ReactiveCocoa

在“避免保留循环”一章中,作者说,为了避免保留循环,我们应该在subscribeNext 块中将self 替换为bself。但是他将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;
  }];

这种做法对吗?为什么?

【问题讨论】:

  • 我认为这只是教程作者的错误。你应该在块内使用weakself。
  • ReactiveCocoa 包含一个相对冗长的文档描述Memory Management。您可能想从那里开始。

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


【解决方案1】:

我们可以用科学来回答!

虽然我称赞那个教程提到了保留周期,但我认为它并没有很好地处理它(更不用说它有问题了)。当我们需要@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 对其有强引用,则UITextField 将在self 释放它之前释放它(可能在[self dealloc] 中)。 (实际上,文本字段可能也在视图层次结构中,因此它需要在它真正消失之前出现,但我们假设这发生在 self 被释放之前(如果我们的视图控制器代码是好)并假装self是唯一保留它的东西)。

如果self 对文本字段有一个引用,那么我们不知道并且需要收集更多信息来弄清楚是什么让它保持活力.在这种情况下,在块中强引用 self 可能会间接导致保留循环,但可能不会。

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

从来没有。因为信号保留的块正在保留self

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

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

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

但是用这些术语来思考它有助于我们以更通用的方式找出这些弱引用何时是必要的——如果你只是说“当你订阅时弱引用 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;
}];

【讨论】:

    【解决方案2】:

    是的,避免块中的保留循环是一种很好的做法。所以将self替换为bself。这很好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-30
      • 2011-07-25
      • 2012-09-19
      • 2011-01-30
      • 2012-08-27
      • 2012-10-06
      • 2016-12-01
      • 1970-01-01
      相关资源
      最近更新 更多