斯威夫特有:
- 强引用
- 弱引用
- 无主引用
无主引用与弱引用有何不同?
什么时候使用无主引用是安全的?
无主引用是否存在安全风险,例如C/C++ 中的悬空指针?
weak
和引用都unowned
不会在引用的对象上创建strong
保留(也就是它们不会增加保留计数以防止 ARC 释放引用的对象)。
但是为什么有两个关键字呢?这种区别与Optional
Swift 语言中内置的类型有关。长话短说:可选类型提供内存安全(这与Swift 的构造函数规则完美配合——为了提供这种好处,这些规则非常严格)。
weak
引用允许它成为可能(当nil
被引用的对象被释放时,这会自动发生),因此您的属性类型必须是可选的 - 所以作为程序员,您有义务在使用它之前检查它(基本上是编译器尽可能地强迫你编写安全代码)。
unowned
参考假定它在其生命周期内永远不会变成nil
。必须在初始化期间设置无主引用 - 这意味着该引用将被定义为非可选类型,无需检查即可安全使用。如果以某种方式被引用的对象被释放,那么当使用无主引用时应用程序将崩溃。
来自苹果文档:
只要该引用在其生命周期中的某个时间点变为 nil 有效,就使用弱引用。相反,当您知道一旦在初始化期间设置引用将永远不会为零时,请使用无主引用。
在文档中,有一些示例讨论了保留周期以及如何打破它们。所有这些示例都是从docs 中提取的。
weak
关键字示例:
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
}
class Apartment {
let number: Int
init(number: Int) { self.number = number }
weak var tenant: Person?
}
现在,对于一些 ASCII 艺术(你应该去看看文档——他们有漂亮的图表):
Person ===(strong)==> Apartment
Person <==(weak)===== Apartment
Person
andApartment
示例显示了两个属性都允许为 nil 的情况,它们有可能导致强引用循环。这种情况最好用弱参考来解决。两个实体都可以存在而不严格依赖另一个实体。
unowned
关键字示例:
class Customer {
let name: String
var card: CreditCard?
init(name: String) { self.name = name }
}
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) { self.number = number; self.customer = customer }
}
在这个例子中,aCustomer
可能有也可能没有CreditCard
,但是 aCreditCard
总是与 a 相关联Customer
。为了表示这一点,Customer
该类具有一个可选card
属性,但CreditCard
该类具有一个非可选(和无主)customer
属性。
Customer ===(strong)==> CreditCard
Customer <==(unowned)== CreditCard
和示例显示了一种情况Customer
,CreditCard
其中一个允许为 nil 的属性和另一个不能为 nil 的属性有可能导致强引用循环。这种情况最好使用无主引用来解决。
苹果的注意事项:
弱引用必须声明为变量,以表明它们的值可以在运行时改变。弱引用不能声明为常量。
还有第三种情况,两个属性都应该总是有一个值,一旦初始化完成,两个属性都不应该为 nil。
在使用闭包时,还有一些经典的保留循环场景要避免。
Q1。“无主引用”与“弱引用”有何不同?
弱参考:
弱引用是一种不会对其引用的实例保持强控制的引用,因此不会阻止 ARC 处理引用的实例。因为弱引用被允许“没有值”,所以你必须将每个弱引用声明为具有可选类型。(苹果文档)
无主参考:
与弱引用一样,无主引用不会对其引用的实例保持强控制。然而,与弱引用不同的是,无主引用被假定为始终具有值。因此,无主引用总是被定义为非可选类型。(苹果文档)
何时使用每个:
只要该引用在其生命周期中的某个时间点变为 nil 有效,就使用弱引用。相反,当您知道一旦在初始化期间设置引用将永远不会为零时,请使用无主引用。(苹果文档)
Q2。什么时候使用“无主引用”是安全的?
如上所述,假定无主引用始终具有值。因此,只有在确定引用永远不会为零时,才应该使用它。Apple Docs 通过以下示例说明了无主引用的用例。
假设我们有两个类Customer
和CreditCard
。客户可以没有信用卡而存在,但信用卡没有客户就不会存在,即可以假设信用卡总是有客户。因此,它们应该具有以下关系:
class Customer {
var card: CreditCard?
}
class CreditCard {
unowned let customer: Customer
}
Q3。“无主引用”是否引用了 C/C++ 中的“悬空指针”之类的安全风险
我不这么认为。
由于无主引用只是保证具有值的弱引用,因此它不应该以任何方式构成安全风险。但是,如果在释放它引用的实例后尝试访问无主引用,则会触发运行时错误,并且应用程序将崩溃。
这是我看到的唯一风险。
如果self在闭包中可以为零,请使用[weak self]。
如果self在闭包中永远不会为零,请使用[unowned self]。
如果在您使用[unowned self]时它崩溃了,那么 self 在该闭包中的某个时刻可能为零,您可能需要使用[weak self]来代替。
查看在闭包中使用strong、weak和unowned的示例:
从链接中提取
几点总结
weak
和引用都unowned
不会影响对象的引用计数。但是弱引用总是可选的,即它可以是 nil,而unowned
引用永远不能是 nil,所以它们永远不会是可选的。使用可选引用时,您将始终必须处理对象为零的可能性。如果是无主引用,您必须确保该对象永远不会为零。使用对 nil 对象的无主引用类似于强制解包 nil 的可选项。
也就是说,在您确定对象的生命周期超过引用的生命周期的情况下,使用无主引用是安全的。如果不是这种情况,最好改用弱引用。
至于问题的第三部分,我不认为无主引用类似于悬空指针。当我们谈论引用计数时,我们通常指的是对象的强引用计数。同样,swift 维护对象的无主引用计数和弱引用计数(弱引用指向称为“side table”而不是对象本身的东西)。当强引用计数达到零时,对象将被取消初始化,但如果无主引用计数大于零,则无法释放它。
现在,悬空指针是指向已被释放的内存位置的东西。但是在swift中,由于只有对对象的无主引用才能释放内存,所以它不会导致悬空指针。
有很多文章更详细地讨论了快速内存管理。这是一个。
无主引用是一种弱引用,用于两个对象之间的 Same-Lifetime 关系,此时一个对象只能由另一个对象拥有。这是一种在对象与其属性之一之间创建不可变绑定的方法。
在中级 Swift WWDC 视频中给出的示例中,一个人拥有一张信用卡,而一张信用卡只能有一个持有人。在信用卡上,人不应该是可选的财产,因为你不希望信用卡只有一个所有者。您可以通过将贷方上的持有者属性设为弱引用来打破这个循环,但这也需要您将其设为可选和可变(而不是常量)。在这种情况下,无主引用意味着尽管 CreditCard 在 Person 中没有所有权,但它的生命取决于它。
class Person {
var card: CreditCard?
}
class CreditCard {
unowned let holder: Person
init (holder: Person) {
self.holder = holder
}
}
unowned
当您确定self
永远不会nil
在您当时访问的点时使用self
。
示例(您当然可以直接从 添加目标MyViewController
,但同样,这是一个简单的示例)。:
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let myButton = MyButton { [unowned self] in
print("At this point, self can NEVER be nil. You are safe to use unowned.")
print("This is because myButton can not be referenced without/outside this instance (myViewController)")
}
}
}
class MyButton: UIButton {
var clicked: (() -> ())
init(clicked: (() -> ())) {
self.clicked = clicked
// We use constraints to layout the view. We don't explicitly set the frame.
super.init(frame: .zero)
addTarget(self, action: #selector(clicked), for: .touchUpInside)
}
@objc private func sendClosure() {
clicked()
}
}
weak
有可能self
在nil
您访问时使用self
。
例子:
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NetworkManager.sharedInstance.receivedData = { [weak self] (data) in
print("Can you guarentee that self is always available when the network manager received data?")
print("Nope, you can't. Network manager will be alive, regardless of this particular instance of MyViewController")
print("You should use weak self here, since you are not sure if this instance is still alive for every")
print("future callback of network manager")
}
}
}
class NetworkManager {
static let sharedInstance = NetworkManager()
var receivedData: ((Data) -> ())?
private func process(_ data: Data) {
// process the data...
// ... eventually notify a possible listener.
receivedData?(data)
}
}
缺点unowned
:
缺点weak
:
如果您不确定,请使用weak
. 等等,我的意思是在 StackOverflow 上问你在你的情况下应该做什么!在你不应该的时候一直使用弱只会让你和你的代码的读者感到困惑。