Swift:保持对弱变量的强引用

Swift: keeping a strong reference to a weak variable

我有一个 class 对其委托的引用很弱。在后台操作中,我需要设置委托,对 class 执行操作,然后释放委托。

下面的代码在 Debug 模式下有效,但在 Release 模式下失败,因为在 Release 模式下委托会立即被释放。

protocol DocumentDelegate:class { ... }

class MyDocument {
    weak var delegate:DocumentDelegate?

    func save() {
        assert(self.delegate =! nil)
    }
}

// Later:

// (#1) Using "var" does not work:
var delegate:DocumentDelegate? = InterimDelegate()

let document = MyDocument()

document.delegate = delegate

// Compiled in Release mode, at this time the delegate is already nil!
document.save()

delegate = nil


// (#2) Using "let" does work:
let delegate:DocumentDelegate = InterimDelegate()

let document = MyDocument()

document.delegate = delegate

// Compiled in Release mode, at this time the delegate is already nil!
document.save()

我假设最后一条指令 delegate = nil 会导致编译器保留委托直到那时(即“最后”使用变量的时间)。但是,仔细想想,编译器优化代码并立即释放委托实例确实是有道理的,因为没有其他强引用。

但是,我不明白为什么在使用“let”时编译器在第二种情况下的行为方式不同。在这里,编译器也可以看到委托没有通过其他任何地方的强引用被引用,但它确实会保留它直到块结束。

考虑这个问题的好方法是什么?保持对弱委托的强引用的好方法是什么?

你描述的只是一个强委托。去掉weak,你不是那个意思

如果您需要一个对象只要有一个对它的引用就存在,并且您打算在某个时候手动删除该引用,那就是强引用。对于委托来说重要的是它会在某个时候释放它的引用以避免循环。这通常使用弱引用来完成,因为它使事情变得简单。但是可以通过强引用手动完成。例如,URLSession's delegate 就是这样工作的。

但是,如果您希望委托总是在 save 之后释放,您可能希望在 save 中将其设置为 nil。这比让调用者这样做更好(并且匹配 URLSession 的工作方式;它会在完成时自动释放其委托)。

为了解释您的代码中发生了什么,允许 ARC 在最后一次使用后释放引用。

// Example 1

// delegate is a strong reference
var delegate:DocumentDelegate? = InterimDelegate()

let document = MyDocument()

// Last read of delegate.
document.delegate = delegate
// delegate is released here. `document.delegate` is weak,
// so object is deallocated and set to nil.

// Compiled in Release mode, at this time the delegate is already nil!
document.save()

// This assignment is a no-op. The system was allowed to set it
// to nil earlier, so this doesn't matter.
delegate = nil


// Example 2

// This creates a strong reference
let delegate:DocumentDelegate = InterimDelegate()

let document = MyDocument()

// Last read of delegate.
document.delegate = delegate
// delegate is released here. `document.delegate` is weak,
// so object is deallocated and set to nil.    

// Compiled in Release mode, at this time the delegate is already nil!
document.save()

虽然我完全同意 Rob Napier 的分析,但为了完整起见,我应该指出,您也可以将对象的生命周期明确化:

let delegate = InterimDelegate()

withExtendedLifetime(delegate) {
    let document = MyDocument()
    document.delegate = delegate
    document.save()
}