【问题标题】:Block implicitly retains 'self'; - but is it intended behaviour?Block 隐式保留'self'; - 但这是预期的行为吗?
【发布时间】:2018-03-30 22:18:25
【问题描述】:

昨天我最新的 iOS 版本在 Xcode 上运行时没有出现警告。在一夜之间升级到版本 9.3 (9E145) 后,我收到了多个警告。当我在answer (1) 之后尝试self->score 到类似问题时,警告消失了。

但在最近的answer (2) 中,对于同一问题,问题是通过更改设置来解决的。目前我对Apple LLVM 9.0 - Warnings -Objective C and ARC 的设置是

在块内隐式保留“自我”是

但我不明白Block implicitly retains 'self' 在代码下面的上下文中是什么意思,所以我不能说这种行为是否是“有意的”。或者我是否解决了问题或只是隐藏了它。或者答案 1 是否会比答案 2 更好。

有人可以解释一下Block implicitly retains 'self' 在这种情况下的含义吗?谢谢。

score.alpha = 1.0;

if (sequenceState >= STATES_Count)
{
    [GraphicScore animateWithDuration:8.0f
                                delay:1.0f
                              options:UIViewAnimationOptionCurveEaseOut
                           animations:^{self->score.alpha = 0.0;} // animations:^{score.alpha = 0.0;}
                           completion:^(BOOL finished){ }];
}
[self addSubview:score];

【问题讨论】:

  • 并不是self->score.alphascore.alpha更好 — 从技术上讲,它是完全相同的代码 — 很明显,该块隐含地保留了 @987654330 @。这通常是一个需要识别的非常微妙的问题。写self->property 使它更明显,从而更容易在你的代码中发现这个(潜在的)问题。就像人们写(void)SomeFunction();明显故意忽略返回值。

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


【解决方案1】:

这个关于隐式引用 self 的警告很有用,因为如果没有这个警告,当浏览代码时,并不总是很明显哪些块有引入强引用循环的风险,哪些没有。通过鼓励程序员明确这些 self 引用(例如在 Swift 等安全编程语言中所要求的),您最终会得到可以清楚地看到强引用循环是否是潜在问题的代码。

因此,我鼓励您打开警告,但继续使用 self-> 或如果使用属性,则使用 self. 显式地使用隐式 self 引用(即 ivars),如您引用的第一个答案。

然后,您可以查看 self 闭包的单独使用情况,并确保它们不会引入任何强引用循环的真正风险。如果他们这样做,您可以酌情采用weakSelfweakSelf/strongSelf 模式。

【讨论】:

  • Rob,这让事情变得非常清楚。并感谢您的参考。
  • 嘿@Rob,你有什么资源可以指点我,以便更好地理解这些自我引用何时引入保留周期?我理解保留周期的概念 - 两个对象相互之间有很强的引用,因此即使您删除了对这些对象的引用,它们也会阻止彼此解除分配。我很难弄清楚这到底是如何与积木一起工作并避免危险的。在更新项目设置以显示此警告后,我收到了 300 条警告,大部分是由于在 API 调用完成块中使用了 IVars,这些块位于实例方法中。
  • 有关 Swift 的讨论,请参阅 The Swift Programming Language: Automatic Reference Counting,特别是标题为 Strong Reference Cycles for Closures 的部分以及之后的部分,关于解决这些循环。
  • @JakeT。 - Swift 的概念类似于 Objective-C 的概念,这些参考资料提供了关于该主题的相当广泛的讨论。但是,如果您正在寻找 Objective-C 文档,请参阅 Programming with Objective-C: Working with Blocks: Avoid Strong Reference Cycles when Capturing self
  • 谢谢@Rob。我在那里通读了 Swift 文档。关于惰性变量实例化的一点很有帮助,因为这是我选择的一种模式,但我在其中引用了很多 self。我确信我有很多强大的参考周期可以通过添加一个简单的[unowned self] in 行来打破。现在我只关心如何处理作为完成处理程序传递给异步 API 方法的闭包。这些最常出现在我的 VC 和对象的实例方法中。异步 API 调用是类方法和实例方法的混合。我现在有限的理解告诉我,
【解决方案2】:

给定:

- (void)bobIsYourUncle {
   ^{score.alpha = 0.0;}();
}

其中score 是一个实例变量,该实例变量通过self 访问。因为该块可能在某处排队并稍后执行,所以该块在创建块时保留self

因为 ivar 访问 不可见 通过 self 取消引用,隐式保留 在代码中有点不明显。编译器在 ivar 前面添加 self-> 的相当丑陋的建议至少可以明显看出 self 被块捕获。

所以,是的,这是正确的行为。而且,是的,它有时是需要的。您可以通过在创建块之前(在局部变量中)获取 ivar 的值来避免它,但您还必须知道这样做会更改获取 ivar 值的时间,这可能会导致行为改变。

【讨论】:

  • self-> 前面的 ivar 在什么意义上是丑陋的?即使 Xcode 在一两个地方发白,它也确实有效
  • @Greg - 用于解除引用 ivars 的 -> 模式有效,但 Apple 多年来一直鼓励使用属性(带点表示法)(请参阅 WWDC 2012 视频 Modern Objective-CMigrating to Modern Objective-C)并且往往受到许多人的青睐。这是一种更现代的模式,可以与弱引用、优雅的句柄 nil 引用等一起使用。-> 对我们许多人来说感觉有点不合时宜。
  • @Greg 对于我们自 80 年代后期以来一直在敲打括号的 ObjC 灰胡子来说,这很丑陋。 ;)。在这个现代的块中,编译器会仔细检查我的一举一动和 ARC,我真的接受了->。 Rob 所说的绝对正确,尽管编译器建议修复 self-> 是明确完成的,因为它保证不会改变运行时行为,而从直接切换到通过方法调用访问可能会发生这种情况。
猜你喜欢
  • 2014-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多