Apple 的 Swift 编程语言指南 除了 unowned(safe)
和
unowned(unsafe)
之外,还提到了 capture 说明符
weak
和 unowned
。
我(认为我)理解
weak
和unowned
之间的区别;但是unowned(safe)
和unowned(unsafe)
有什么区别呢?导游没说。
请:不要依赖于简单地陈述 Objective-C 的等价物。
据我了解,虽然我找不到Apple的确切来源,但
unowned
可以分为两种口味,safe
和unsafe
。
裸露的
unowned
是unowned(safe)
:它是一个特殊包装的引用,当引用已释放的实例时会抛出异常。
特殊情况是
unowned(unsafe)
:它是 Objective C 的 @property (assign)
或 __unsafe_unretained
的 Swift 等价物。它不应该在 Swift 程序中使用,因为它的目的是桥接到用 Objective C 编写的代码。
因此,在查看 Cocoa 类的导入包装器时,您会看到
unowned(unsafe)
,但除非必须,否则不要使用它,并且您会知道何时需要。
更新
__unsafe_unretained
是一个简单的指针。它不会知道所指向的实例何时被释放,因此当它被取消引用时,底层内存可能是垃圾。
如果存在使用已释放的
__unsafe_unretained
变量的缺陷,您将看到不稳定的行为。有时该内存位置足够好,因此代码可以运行,有时它会被部分覆盖,因此您会遇到非常奇怪的崩溃,有时该内存位置将包含一个新对象,因此您会遇到无法识别的选择器异常。
指定一个引用,该引用不会使被引用的对象保持活动状态,并且当没有对该对象的强引用时,不会将其设置为nil。如果它引用的对象被释放,指针就会悬空。__unsafe_unretained
这里引用来自 Apple 开发者论坛:
unowned
vs unowned(safe)
vs unowned(unsafe)
是一个非拥有引用,它在访问时断言 该对象仍然活着。这有点像弱可选引用 每次访问时都会用unowned(safe)
隐式解开它。x!
就像 ARC 中的unowned(unsafe)
——它是非拥有的 引用,但没有运行时检查该对象是否仍然存在 访问时,悬空引用将进入垃圾内存。 目前,__unsafe_unretained
始终是unowned
的同义词,但 目的是在unowned(safe)
中将其优化为unowned(unsafe)
禁用运行时检查时构建。-Ofast
变量在已使用属性解除分配时被访问:
一个简单的定义。这会消除混乱。
-- 无主属性:如果您在释放其引用的实例后尝试访问无主引用,您的程序将崩溃。
-- unowned(Unsafe) 属性:如果在它引用的实例被释放后尝试访问一个不安全的无主引用,你的程序将尝试访问该实例曾经所在的内存位置,这是一个不安全的操作。 (不保证这是否会执行或崩溃)
简单来说
unowned(safe)
或简单地 unowned
- 如果我们尝试引用已释放的对象,则会导致应用程序崩溃。
unowned(unsafe)
- 不要使应用程序崩溃,而是尝试找到在取消初始化之前存储引用对象的内存位置。
考虑示例 -
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
print("It's Credit card is - ", card)
}
}
class CreditCard {
let number: UInt64
unowned(unsafe) let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Card #\(number) is being deinitialized")
print("It's customer is - ", customer.name)
}
}
var customer: Customer? = Customer(name: "Stackoverflow")
customer!.card = CreditCard(number: 1234_5678_9012_3456, customer: customer!)
customer = nil
当您在 Playground 中运行此代码时,您将得到以下输出 -
Stackoverflow is being deinitialized
It's Credit card - Optional(__lldb_expr_16.CreditCard)
Card #1234567890123456 is being deinitialized
It's customer - Stackoverflow
这里要检查的主要事情是,即使 Stackoverflow 已被取消初始化,但我们仍然可以在 CreditCard 的 deinit 调用中打印它
如果您尝试使用
unowned
而不是 unowned(unsafe)
调用相同的代码,那么它将崩溃并显示以下日志
Stackoverflow is being deinitialized
It's Credit card - Optional(__lldb_expr_20.CreditCard)
Card #1234567890123456 is being deinitialized
Fatal error: Attempted to read an unowned reference but object 0x600000291830 was already deallocated