手动内存管理中的弱/强“引用”

4
假设使用手动内存管理(又称非ARC)的以下情景:
我有一个视图控制器(VC),它将一个块传递给一个类方法。在块被执行之前,VC从UINavigationController中弹出。通过__block MyVC *weakSelf = self形式传递弱引用到块中,然后将其转换为MyVC *strongSelf = weakSelf(也称为weak/strong dance)。该块不会被任何参与者保留。
在这种情况下,我在我的代码中看到的是:
1.当VC弹出时,dealloc将被调用。
2.最终将调用该块。
3.应用程序崩溃,因为我正在访问垃圾(strongSelf指向它)。
我的问题是:我不希望我的VC一直保持活动状态,直到块最终执行,但一旦块被执行,我想确认strongSelf是否有效。
我查看了这个 ( 苹果公司提供的非平凡示例),它不能正常工作,因为它是为ARC设计的。那么如何在MMM中实现相同的行为呢?理想情况下,我希望它能像__weak一样:如果retainCount达到零,我希望它的引用指向空值而不是垃圾。
阅读了苹果公司的内容后:
在某些情况下,如果类不兼容__weak,则可以使用__unsafe_unretained。但是,对于非平凡的循环,这可能变得不切实际,因为很难或不可能验证__unsafe_unretained指针仍然有效并且仍然指向相应的对象。
由于我无法访问__weak,那么我想做的事情是否可能?

@trojanfoe,关键是要知道在块内部调用方法是否安全。如果视图控制器已经不存在,那么程序会崩溃;如果它仍然存在,那么程序将按预期工作。 - Rui Peres
1
你可以实现你想要达到的目标,但这并不是一件简单的事情。我正在撰写答案,因为我几年前也曾面临过这个问题。请继续关注! - Alex MDC
@trojanfoe 如果我这样做,我就必须在尝试执行之前到处验证块是否为“NULL”。 - Rui Peres
那个代码块可以在三个不同的地方执行,所以我需要在这三个地方进行验证,对吧? - Rui Peres
@trojanfoe 所以我基本上是将这个块包装在另一个块中? :/ - Rui Peres
2个回答

1
我曾经在iOS 4.x时期与这个问题斗争过。没有弱指针帮助,这并不容易!
如果您可以保证块在稍后的主线程上执行(即在从weakSelf分配strongSelf的地方),那么您基本上需要一个存储“isDead”标志的地方,当VC被释放时设置该标志。然后您在使用weakSelf/strongSelf之前检查此标志。一种解决方案是:
  1. 你需要一个类,它的唯一工作就是将“isDead”标志存储在成员变量中,类似于NSMutableBoolean。我不会写出来,它很简单,只需要一个BOOL属性。
  2. 在你的VC的-[NSObject init]方法中,创建这个标志对象的实例,它最初应该设置为false
  3. 然后将这个对象传递给任何您排队等待执行的块,以便由块自动保留/释放(即不需要进行弱/强引用)。在块内部,您检查标志是否仍然是NO,然后再处理weakSelf。
  4. 关键当然是在VC的-[NSObject dealloc]方法中将标志设置为YES,然后释放它。如果有任何块仍在等待执行,它们已经保留了标志对象本身,当它们稍后被执行时,它们将查询标志并发现VC现在已经死亡。

这听起来有些繁琐(而且确实是),但其思想是,“isDead”标志存在于VC范围之外,因此不与其生命周期相关联。至于线程安全性,只要您仅在VC的init/dealloc方法和执行块时(在主线程上,而不是在后台线程上)设置/查询标志,它就是线程安全的。


0
我不想让我的VC一直存活到块最终执行。
但是为什么这很重要呢?一个VC的内存不应该那么大,如果块对VC执行任何UI操作也没关系,因为VC不会被显示出来。
基本上,如果VC没有保留块,那么块就不应该捕获VC的弱引用。使用非零弱引用意味着您保证对象将保持活动状态,而这在这种情况下并不是这样。我建议您不要这样做弱引用的事情。

你正在做一个假设:block在VC上执行任何UI操作也没关系,因为VC根本没有被显示出来,但如果它有一些重量级的副作用呢?比如图片获取?我试图避免不必要的额外工作。 - Rui Peres

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接