弱引用和无主引用之间的区别是什么?

斯威夫特有:

  • 强引用
  • 弱引用
  • 无主的引用

无主引用与弱引用有何不同?

什么时候使用无主引用是安全的?

C/ c++中的无主引用是否存在类似悬空指针的安全风险?

80580 次浏览

无主引用是一种弱引用,用于两个对象之间存在同一生命周期关系的情况下,这时一个对象应该只属于另一个对象。它是一种在对象与其属性之一之间创建不可变绑定的方法。

在中间swift WWDC视频中给出的例子中,一个人拥有一张信用卡,而一张信用卡只能有一个持有人。在信用卡上,人不应该是可有可无的,因为您不希望信用卡只有一个所有者。您可以通过将信用证上的持有人属性设置为弱引用来打破这个循环,但这也要求您将其设置为可选的变量(而不是常量)。在本例中,无主引用意味着尽管CreditCard在Person中没有所有权股份,但它的生命依赖于它。

class Person {
var card: CreditCard?
}


class CreditCard {


unowned let holder: Person


init (holder: Person) {
self.holder = holder
}
}

weakunowned引用都不会在被引用的对象上创建strong保持(也就是说,它们不会增加保留计数以防止ARC释放被引用的对象)。

但为什么只有两个关键词呢?这种区别与Optional类型是Swift语言内置的事实有关。长话短说:可选的类型提供了内存安全性(这与Swift的构造函数规则一起工作很好——为了提供这种好处,Swift的构造函数规则是严格的)。

weak引用允许它成为nil的可能性(这在被引用的对象被释放时自动发生),因此你的属性类型必须是可选的——所以作为程序员,你有义务在使用它之前检查它(基本上编译器会强迫你尽可能多地编写安全代码)。

unowned引用假定它在生命周期内永远不会变成nil。在初始化过程中必须设置无主引用——这意味着该引用将被定义为非可选类型,无需检查即可安全使用。如果被引用的对象以某种方式被释放,那么当使用无主引用时,应用程序将崩溃。

苹果公司的文档:

使用弱引用,当该引用成为有效时 在生命周期的某个时刻为Nil。相反,使用无主的 当你知道引用永远不会为nil时,再引用

在文档中,有一些例子讨论了保留周期以及如何打破它们。所有这些例子都是从的文档中提取的。

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

PersonApartment示例显示了两个属性都被允许为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

CustomerCreditCard示例显示了一种情况,其中一个属性被允许为nil,而另一个属性不能为nil,有可能导致强引用循环。这种情况最好使用无主引用来解决。

苹果公司的说明:

弱引用必须声明为变量,以表明它们的 值可以在运行时更改。弱引用不能声明为 常数。< / p >

还有第三种情况,两个属性都应该有一个值,并且在初始化完成后,两个属性都不应该为nil。

在使用闭包时,还有一些经典的保留周期场景需要避免。

为此,我鼓励你访问苹果公司的文档,或阅读这本书

Q1。“无主引用”与“弱引用”有何不同?

弱引用:

弱引用是指不保持强引用的引用 它引用的实例,因此不会阻止ARC处理 引用的实例。因为弱引用是允许的 " no value "时,必须声明每个弱引用都具有 可选的类型。(苹果文档) < / p >

无主参考:

像弱引用一样,无主引用不会保持强持有 在它引用的实例上。然而,与弱引用不同的是 无主引用假定总是有一个值。正因为如此, 无主引用总是定义为非可选类型。(苹果文档) < / p >

何时使用:

使用弱引用,当该引用成为有效时 在生命周期的某个时刻为Nil。相反,使用无主的 当你知道引用永远不会为nil时,再引用 已在初始化时设置。(苹果文档) < / p >


Q2。什么时候使用“无主引用”是安全的?

如上所述,假定无主引用总是有一个值。所以你应该只在确定引用永远不会为nil时使用它。Apple Docs通过以下示例演示了无主引用的用例。

假设我们有两个类CustomerCreditCard。客户可以没有信用卡而存在,但信用卡不会没有客户而存在,也就是说,可以假设信用卡总是有客户。所以,它们应该有这样的关系:

class Customer {
var card: CreditCard?
}


class CreditCard {
unowned let customer: Customer
}

第三季。在C/ c++中,“无主引用”引用是否像“悬空指针”一样存在安全风险

我不这么想。

由于无主引用只是保证有值的弱引用,因此无论如何都不应构成安全风险。然而,如果你试图访问一个无主引用后,它引用的实例被释放,你将触发一个运行时错误,应用程序将崩溃。

这是我看到的唯一风险。

苹果文档链接

如果自我在闭包中可以为nil,则使用(弱自我)

如果自我在闭包中永远不会为nil,则使用(无主的自我)

如果当你使用(无主的自我)时它崩溃了,那么self可能在闭包中的某个时刻为nil,你可能需要使用(弱自我)来代替。

看看在闭包中使用<强大的>强大的无主的例子:

https://developer.apple.com/library/ios/documentation/swift/conceptual/swift_programming_language/AutomaticReferenceCounting.html

链接的摘录

结论很少

  • 来决定你是否需要担心强,弱,或 ,问,“我正在处理引用类型”。如果你在工作 对于结构或枚举,ARC不为这些类型管理内存 您甚至不需要担心指定weak或u主for 这些常量或变量。
  • 强引用适用于父对象引用子对象的层次关系,反之则不然。事实上,强引用在大多数情况下是最合适的引用类型。
  • 当两个实例可选地相互关联时,请确保 其中一个实例持有对另一个实例的弱引用
  • 当两个实例之间的关联方式使其中一个实例 实例离不开另一个实例,实例离不开 强制依赖项需要持有对另一个的无主引用 李实例。< / >

当你确定在你访问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时,有可能selfnil时,使用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的情况下一直使用weak只会让您和代码的读者感到困惑。

weakunowned引用都不会影响对象的引用计数。但是弱引用总是可选的,即它可以为nil,而unowned引用永远不会为nil,因此它们永远不会是可选的。当使用可选引用时,你总是要处理对象为nil的可能性。在无主引用的情况下,你必须确保对象永远不是nil。对nil对象使用无主引用类似于强制打开一个为nil的可选对象。

也就是说,在确定对象的生命周期大于引用的生命周期时,使用无主引用是安全的。如果不是这样,最好使用弱引用。

至于问题的第三部分,我不认为无主引用类似于悬浮指针。当我们谈论引用计数时,我们通常指的是对象的强引用计数。类似地,swift维护对象的无主引用计数和弱引用计数(弱引用指向一个称为“side table”的东西,而不是对象本身)。当强引用计数为零时,该对象将被去初始化,但如果无主引用计数大于零,则无法释放该对象。

悬空指针指向一个已经被释放的内存位置。但是在swift中,由于内存只有在对象有一个无主引用时才能被释放,所以它不会导致悬空指针。

有很多文章更详细地讨论了swift内存管理。在这里是1。