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()
}
我有一个 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()
}