【问题标题】:Block capturing 'self' strongly强烈阻止捕获“自我”
【发布时间】:2013-10-09 13:43:27
【问题描述】:

我对按钮有以下操作,它可以切换对象是显示为收藏还是非收藏:

- (IBAction)addToFavorites:(UIButton *)sender {
  if ([object isFavorite]) {
    [_apiManager removeFromFavorite:[object ID] withCompletion:^ {
      [_favoriteButton setImage:[UIImage imageNamed:@"favorite"] forState:UIControlStateNormal];
    }];
  }

  else {
    [_apiManager addToFavorite:[object ID] withCompletion:^ {
      [_favoriteButton setImage:[UIImage imageNamed:@"favorite_yellow"] forState:UIControlStateNormal];
    }];
  }
}

两个完成块是相同的,除了图像名称。 XCode 向else 案例发出警告:Capturing 'self' strongly in this block is likely to lead to a retain cycle 并指向_favoriteButton。 但是,if 条件为 true 时不会发生同样的情况。

我想这两种情况都应该出现警告,或者都不应该出现警告,我不明白为什么只有后一种情况才会出现。这是一个 Xcode 错误吗?两者都会导致保留周期吗?

【问题讨论】:

  • 如果注释掉else 分支,它不会在剩余的分支中给出警告吗?如果是这种情况,您可能刚刚看到编译器将相同的警告减少为一个。
  • 我认为这是一个 xcode 错误,请尝试取消注释第二个块并仅使用第一个块运行分析。是的,这可能会导致两种情况下的保留周期。
  • Clang - Blocks retain cycle from naming convention? 的可能重复项 - ARC 编译器确实使用命名约定来决定捕获 self 是“好”还是“坏”。
  • XCode 警告有时是错误的。您是否尝试过清理项目并重新构建?
  • @Guilherme:ARC 编译器使用命名约定来决定是否发出警告。这似乎是一种未记录的启发式方法,我仅通过检查编译器源代码才发现这一点,请参阅我对“可能重复”的回答。这可能会随着新版本的发布而改变。 - 如果你实际上有一个“坏”的保留周期不依赖于方法名称。这取决于保留周期是“临时的”(即最终调用并释放块)还是“永久的”。

标签: ios objective-c objective-c-blocks retain-cycle


【解决方案1】:

_favoriteButton 是一个 ivar。它由您的类的特定实例拥有,因此使用它会捕获块中的当前self (Reference to instance variables inside a block)

相反,您应该创建一个对 self 的弱引用,并使用属性访问器,如下所示:

- (IBAction)addToFavorites:(UIButton *)sender {
  __weak YourViewControllerClass *weakSelf = self;
  if ([object isFavorite]) {
    [_apiManager removeFromFavorite:[object ID] withCompletion:^ {
      [weakSelf.favoriteButton setImage:[UIImage imageNamed:@"favorite"] forState:UIControlStateNormal];
    }];
  }

  else {
    [_apiManager addToFavorite:[object ID] withCompletion:^ {
      [weakSelf.favoriteButton setImage:[UIImage imageNamed:@"favorite_yellow"] forState:UIControlStateNormal];
    }];
  }
}

【讨论】:

  • 在这种情况下,这很可能是正确的解决方案。 - 但是,如果保证最终调用完成块,并且捕获的对象应该保持活动状态,那么通常可能需要捕获自我。 ARC 编译器使用命名约定来决定是否发出警告,请参阅上面提到的“可能重复”。 -set<Key>-add<Key> 之类的方法名称不会引起警告,而其他所有方法名称都会。
  • 我注意到了这种行为,但不知道 ARC 使用的命名约定,感谢您提供这些宝贵的信息!
  • 是的@Vinzzz,我知道如何解决循环保留问题。我的问题实际上是关于为什么只有第二个块出现警告。谢谢。
  • 您不必使用属性访问器。您可以直接访问实例变量。虽然您必须首先检查该对象是否不是nil
  • @newacct 你确实使用属性访问器。如果您检查重复此问题的问题,您会发现使用对 self 的弱引用是必要的。如果您直接访问 ivar,则该块将保留 self 以为自己提供指向 ivar 的强指针。它不保留 ivar,它保留 ivar 所在的对象。
【解决方案2】:

实际上,在这个例子中并不清楚是否存在保留周期。如果将完成块声明为_apiManager 的属性,则会发生保留周期。如果它只是方法范围内的一个块(只是一个方法参数),则没有保留周期,但是 XCode 不够聪明,无法检测这些情况并警告您可能存在保留周期。

关于您的问题,这只是顺序,它会在第一个保留周期中警告您,第二个无关紧要,因为该块已经从第一个块中保留了自己。如果您使用__weak self 修复第一个警告,它将在第二个块上发出警告。

【讨论】:

    猜你喜欢
    • 2013-01-11
    • 2016-12-17
    • 2012-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-13
    • 2020-07-16
    相关资源
    最近更新 更多