Swift 中“无主(安全)”和“无主(不安全)”有什么区别?

问题描述 投票:0回答:5

Apple 的 Swift 编程语言指南 除了 unowned(safe)

unowned(unsafe)
之外,还提到了
capture 说明符
weak
unowned

我(认为我)理解

weak
unowned
之间的区别;但是
unowned(safe)
unowned(unsafe)
有什么区别呢?导游没说。


请:不要依赖于简单地陈述 Objective-C 的等价物。

memory-management swift automatic-ref-counting
5个回答
45
投票

据我了解,虽然我找不到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
变量的缺陷,您将看到不稳定的行为。有时该内存位置足够好,因此代码可以运行,有时它会被部分覆盖,因此您会遇到非常奇怪的崩溃,有时该内存位置将包含一个新对象,因此您会遇到无法识别的选择器异常。

过渡到 ARC 发行说明

__unsafe_unretained
指定一个引用,该引用不会使被引用的对象保持活动状态,并且当没有对该对象的强引用时,不会将其设置为nil。如果它引用的对象被释放,指针就会悬空。


28
投票

这里引用来自 Apple 开发者论坛

unowned
vs
unowned(safe)
vs
unowned(unsafe)

unowned(safe)
是一个非拥有引用,它在访问时断言 该对象仍然活着。这有点像弱可选引用 每次访问时都会用
x!
隐式解开它。
unowned(unsafe)
就像 ARC 中的
__unsafe_unretained
——它是非拥有的 引用,但没有运行时检查该对象是否仍然存在 访问时,悬空引用将进入垃圾内存。 目前,
unowned
始终是
unowned(safe)
的同义词,但 目的是在
unowned(unsafe)
中将其优化为
-Ofast
禁用运行时检查时构建。


13
投票

变量在已使用属性解除分配时被访问:

无主

  • 程序知道它无效,并立即崩溃。
  • 行为已定义。

无主(不安全)

  • 程序什么都不知道。
  • 可能会立即崩溃。
  • 它可能会访问未知的内存地址并具有奇怪的状态,直到它在意外的位置死亡。
  • 行为是未定义。生活变得更加艰难。

5
投票

一个简单的定义。这会消除混乱。

-- 无主属性:如果您在释放其引用的实例后尝试访问无主引用,您的程序将崩溃。

-- unowned(Unsafe) 属性:如果在它引用的实例被释放后尝试访问一个不安全的无主引用,你的程序将尝试访问该实例曾经所在的内存位置,这是一个不安全的操作。 (不保证这是否会执行或崩溃)


0
投票

简单来说

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
© www.soinside.com 2019 - 2024. All rights reserved.