斯威夫特有:
强引用
弱引用
无主引用
无主引用与弱引用有何不同?
什么时候使用无主引用是安全的?
无主引用在 C/C++ 中是否存在类似 dangling pointers 的安全风险?
unowned
,对于 Apple 类,使用 weak
,因为我们无法确定它的作用
weak
和 unowned
引用都不会在引用的对象上创建 strong
保留(也就是它们不会增加保留计数以防止 ARC 解除分配引用的对象)。
但是为什么有两个关键字呢?这种区别与 Optional
类型是 Swift 语言中内置的事实有关。关于它们的长话短说:optional types 提供内存安全(这与 Swift's constructor rules 配合得很好 - 为了提供这种好处而严格)。
weak
引用允许它变为 nil
(当引用的对象被释放时这会自动发生),因此您的属性类型必须是可选的 - 所以作为程序员,您有义务在之前检查它您使用它(基本上编译器会尽可能多地强迫您编写安全代码)。
unowned
引用假定它在其生命周期内永远不会变为 nil
。必须在初始化期间设置无主引用 - 这意味着该引用将被定义为非可选类型,无需检查即可安全使用。如果以某种方式被引用的对象被释放,那么当使用无主引用时应用程序将崩溃。
从 Apple docs:
只要该引用在其生命周期中的某个时间点变为 nil 有效,就使用弱引用。相反,当您知道一旦在初始化期间设置引用将永远不会为零时,请使用无主引用。
在文档中,有一些示例讨论了保留周期以及如何打破它们。所有这些示例都是从 the 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 艺术(你应该去 see the docs - 他们有漂亮的图表):
Person ===(strong)==> Apartment
Person <==(weak)===== Apartment
Person
和 Apartment
示例显示了两个属性都允许为 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 }
}
在此示例中,Customer
可能有也可能没有 CreditCard
,但 CreditCard
将始终与 Customer
相关联。为了表示这一点,Customer
类有一个可选的 card
属性,但 CreditCard
类有一个非可选(且无主)的 customer
属性。
Customer ===(strong)==> CreditCard
Customer <==(unowned)== CreditCard
Customer
和 CreditCard
示例显示了一种情况,其中一个允许为 nil 的属性和另一个不能为 nil 的属性有可能导致强引用循环。这种情况最好使用无主引用来解决。
苹果的注意事项:
弱引用必须声明为变量,以表明它们的值可以在运行时改变。弱引用不能声明为常量。
还有第三种情况,两个属性都应该总是有一个值,一旦初始化完成,两个属性都不应该为 nil。
在使用闭包时,还有一些经典的保留循环场景要避免。
为此,我鼓励您访问 Apple docs,或阅读 the book。
Q1。 “无主引用”与“弱引用”有何不同?
弱参考:
弱引用是一种不会对其引用的实例保持强控制的引用,因此不会阻止 ARC 处理引用的实例。因为弱引用被允许“没有值”,所以你必须将每个弱引用声明为具有可选类型。 (苹果文档)
无主参考:
与弱引用一样,无主引用不会对其引用的实例保持强控制。然而,与弱引用不同的是,无主引用被假定为始终具有值。因此,无主引用总是被定义为非可选类型。 (苹果文档)
何时使用每个:
只要该引用在其生命周期中的某个时间点变为 nil 有效,就使用弱引用。相反,当您知道一旦在初始化期间设置引用将永远不会为零时,请使用无主引用。 (苹果文档)
Q2。什么时候使用“无主引用”是安全的?
如上所述,假定无主引用始终具有值。因此,只有在确定引用永远不会为零时,才应该使用它。 Apple Docs 通过以下示例说明了无主引用的用例。
假设我们有两个类 Customer
和 CreditCard
。客户可以没有信用卡而存在,但信用卡没有客户就不会存在,即可以假设信用卡总是有客户。因此,它们应该具有以下关系:
class Customer {
var card: CreditCard?
}
class CreditCard {
unowned let customer: Customer
}
Q3。 “无主引用”是否引用了 C/C++ 中的“悬空指针”之类的安全风险
我不这么认为。
由于无主引用只是保证具有值的弱引用,因此它不应该以任何方式构成安全风险。但是,如果在释放它引用的实例后尝试访问无主引用,则会触发运行时错误,并且应用程序将崩溃。
这是我看到的唯一风险。
unowned
作为父母的属性。弱反之亦然。很好的解释@myxtic! unowned
引用只是 weak
保证具有价值的引用!
如果 self 在闭包中可以为零,请使用 [weak self]。
如果 self 在闭包中永远不会为零,请使用 [unowned self]。
如果在您使用 [unowned self] 时它崩溃了,那么 self 在该闭包中的某个时刻可能为零,您可能需要使用 [weak self] 来代替。
查看在闭包中使用强、弱和无主的示例:
self
怎么可能为零?
link 的摘录
几点总结
要确定您是否需要担心强、弱或无主,请询问“我在处理引用类型吗”。如果您使用 Structs 或 Enums,ARC 不会管理这些类型的内存,您甚至无需担心为这些常量或变量指定弱或无主。
强引用在父引用子的层次关系中很好,反之则不行。事实上,强引用在大多数情况下是最合适的引用。
当两个实例可选地相互关联时,请确保其中一个实例持有对另一个实例的弱引用。
当两个实例以这样的方式相关时,其中一个实例没有另一个就不能存在,具有强制依赖关系的实例需要持有对另一个实例的无主引用。
weak
和 unowned
引用都不会影响对象的引用计数。但是弱引用总是可选的,即它可以是 nil,而 unowned
引用永远不能是 nil,所以它们永远不会是可选的。使用可选引用时,您将始终必须处理对象为零的可能性。如果是无主引用,您必须确保该对象永远不会为零。使用对 nil 对象的无主引用类似于强制解包 nil 的可选项。
也就是说,在您确定对象的生命周期超过引用的生命周期的情况下,使用无主引用是安全的。如果不是这种情况,最好改用弱引用。
至于问题的第三部分,我不认为无主引用类似于悬空指针。当我们谈论引用计数时,我们通常指的是对象的强引用计数。同样,swift 维护对象的无主引用计数和弱引用计数(弱引用指向称为“side table”而不是对象本身的东西)。当强引用计数达到零时,对象将被取消初始化,但如果无主引用计数大于零,则无法释放它。
现在,悬空指针是指向已被释放的内存位置的东西。但是在swift中,由于只有对对象的无主引用才能释放内存,所以它不会导致悬空指针。
有很多文章更详细地讨论了快速内存管理。 Here 是一。
无主引用是一种弱引用,用于两个对象之间的 Same-Lifetime 关系,此时一个对象只能由另一个对象拥有。这是一种在对象与其属性之一之间创建不可变绑定的方法。
在中级 Swift WWDC 视频中给出的示例中,一个人拥有一张信用卡,而一张信用卡只能有一个持有人。在信用卡上,人不应该是可选的财产,因为你不希望信用卡只有一个所有者。您可以通过将贷方上的持有者属性设为弱引用来打破这个循环,但这也需要您将其设为可选和可变(而不是常量)。在这种情况下,无主引用意味着尽管 CreditCard 在 Person 中没有所有权,但它的生命取决于它。
class Person {
var card: CreditCard?
}
class CreditCard {
unowned let holder: Person
init (holder: Person) {
self.holder = holder
}
}
当您确定在您访问 self
时 self
永远不会是 nil
时,请使用 unowned
。
示例(您当然可以直接从 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()
}
}
如果在您访问 self
时 self
可能是 nil
,请使用 weak
。
例子:
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
的缺点:
比弱更有效
您可以(好吧,您被迫)将实例标记为不可变(从 Swift 5.0 开始不再存在)。
向您的代码的读者表明:此实例与 X 有关系,没有它就无法生存,但是如果 X 走了,我也走了。
weak
的缺点:
比无主更安全(因为它不会崩溃)。
可以与 X 建立一种双向的关系,但两者都可以相互独立。
如果您不确定,请使用 weak
。 等一下,我的意思是在 StackOverflow 上询问您在这种情况下应该做什么!在你不应该的时候一直使用弱只会让你和你的代码的读者感到困惑。
不定期副业成功案例分享
weak var Person?
与var Person?
有什么区别?