【问题标题】:Blocks in non-trivial cycles非平凡循环中的块
【发布时间】:2013-12-16 19:25:49
【问题描述】:

我读到了article,但我对下面的段落有点困惑。

Apple 文档说“但是,对于非平凡的循环,您应该使用”这种方法:

MyViewController *myController = [[MyViewController alloc] init…];
// ...
MyViewController * __weak weakMyController = myController;
myController.completionHandler =  ^(NSInteger result) {
    MyViewController *strongMyController = weakMyController;
    if (strongMyController) {
        // ...
        [strongMyController dismissViewControllerAnimated:YES completion:nil];
        // ...
    }
    else {
        // Probably nothing...
    }
};

首先,这个例子在我看来是错误的。自己怎么可能 如果块本身保留在 完成处理程序属性? completionHandler 属性可以是 声明为 assign 或 unsafe_unretained 以允许对象 在块被传递后释放。我看不到原因 这样做。如果其他对象需要该对象(自身),则该块 传递的应该保留对象,因此块 不应分配给属性。不应该使用 _weak/_strong 用法 参与此案。

  1. 他说“如果其他对象需要对象(自身):” 他这里的需要是什么意思?是这样的吗:他们需要自我,因为他们访问需要自我的块(传递给他们的块),所以他们需要自我。如果不是,他是什么意思?

  2. 然后他说“因此不应将块分配给属性”。 但是,如果有多个对象在未来某个未定义的时间需要阻塞怎么办?因此,我们可以通过从该属性中获取来将块传递给他们。

我是不是搞错了?

【问题讨论】:

  • 我认为这篇文章令人困惑。此示例中的块不需要需要 self(顺便说一句,我必须假设self 他们的意思是myController)。使用weakMyController 的重点不是保留,因此不需要self。 Apple 所说的“非平凡块”是指多次使用弱引用的块。在这种情况下,您通常希望该引用在块的持续时间内有效。这就是拥有MyViewController *strongMyController = weakMyController;的原因
  • 虽然我理解为什么 Alberto 对 Apple 文档中那个糟糕的例子感到苦恼,但他对问题的诊断和对潜在补救措施的分析存在缺陷。

标签: ios objective-c memory-management automatic-ref-counting objective-c-blocks


【解决方案1】:

文章的作者有点糊涂了,有些东西倒退了。

如果块本身是 保留在 completionHandler 属性中?

如果块保留在completionHandler 中,则意味着self 对该块具有强引用,这意味着只要self 存在,块就处于活动状态——而不是相反。 self 保留块与 self 是否被释放无关。

completionHandler 属性可以声明为 assign 或 unsafe_unretained 允许对象在 块被传递。

同样,completionHandler 是强引用还是弱引用会影响块的生命周期。它与self的生命周期所指向的对象无关。

如果其他对象需要对象(self),则传递的块 周围应该保留对象,因此块不应该 分配给一个属性。

没有。对象保留块。该块不保留对象。

在大多数情况下是否需要代码示例中显示的模式是非常值得商榷的。但是,作者的推理是错误的。

【讨论】:

  • 我的文章中显然没有说清楚,对此我深表歉意。
  • 有些句子写得不好,事实上我同意你的解释。关于你的 3 条报价,这些是我最糟糕、最令人困惑和最不必要的考虑。我刚刚更新了我网站上的文章。
【解决方案2】:

块保留其中提到的变量,以便它们可以在它们首次出现的上下文中执行。当这些保留的块变量之一直接(平凡)或间接(非平凡)保留块时,您会得到一个循环。

例如,当 objectA 拥有一个提到 objectA 的块时,我们得到一个平凡的循环:objectA -> block -> objectA。

// objectA owns a block property (declared as copy) called theBlock
objectA.theBlock = ^{
    // anything in the current scope can be mentioned safely in here, except:
    [objectA doSomething];
};

文章的建议应该是通过在块中使用objectA指针的未保留副本来打破保留循环...

__unsafe_unretained ObjectAType *objectAUnretainedCopy = objectA;
// in an ARC project, @Rob points out that this is probably better qualified as __weak
objectA.theBlock = ^{
    // now we can mention anything in the current scope, including:
    [objectAUnretainedCopy doSomething];
};

现在objectA仍然保留block,但是这个改进的block没有保留objectA。

不平凡的循环也是如此,只是拥有更长的所有权链,这使得循环更难识别。假设 objectB 保留一个指向 objectA 的保留(强)指针...

objectB.myObjectA = objectA;

现在在 objectA 的块中提到 objectB 是错误的,因为你最终会得到一个循环: objectB -> objectA -> theBlock -> objectB.

objectA.theBlock = ^{
    // anything in the current scope can be mentioned safely in here, except:
    [objectB doSomething];
};

同样的解决方案适用。不要直接引用objectB,而是使用未保留的副本...

__unsafe_unretained ObjectBType *objectBUnretainedCopy = objectB;
// again, per @Rob, or __weak

【讨论】:

  • 我想知道为什么您建议 __unsafe_unretained 而不是那篇文章所提倡的 __weak。在这种情况下,我看不到__unsafe_unretained 的任何优势,我可以设计ObjectA 的实现,它可能会给你带来麻烦(而且我们似乎不想编写代码取决于@987654330 的内部实现细节@)。使用__weak 似乎更谨慎。
  • 我养成了使用 ARC 之前的块的习惯。我想最好改成弱。 (虽然我想不出它会破坏的装置)。我浏览了这篇文章,并没有发现它特别清晰。似乎周期应该比作者暗示的更容易描述和更容易治愈。
  • 同意所有事项。如果您想查看在此示例中如何使用__unsafe_unretainedEXC_BAD_ACCESS 结尾的示例,请告诉我(尽管我们可能希望将其移至聊天)。虽然在这种情况下需要一些设计(部分原因是 Alberto(不满意)努力解决的 Apple 示例代码的认知失调),但在许多实际的现实世界场景中,__unsafe_unretained 方法很容易导致问题weakSelf/strongSelf 模式简单地避免了。
【解决方案3】:

请参考以下博客,它是有一定道理的。它正在以更好的方式解释它。

https://dhoerl.wordpress.com/2013/04/23/i-finally-figured-out-weakself-and-strongself/

【讨论】:

  • 这篇文章不错,但没有详细说明以帮助 OP。这篇文章要好得多:logicsector.com/ios/…“因为 Clang 可能会决定在逐个语句的基础上重新评估对象保留”
  • 我同意logicsector.com/ios/… 更好地解释所谓的“强/弱舞蹈”......
猜你喜欢
  • 2013-10-24
  • 1970-01-01
  • 1970-01-01
  • 2019-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-30
  • 1970-01-01
相关资源
最近更新 更多