ChatGPT解决这个技术问题 Extra ChatGPT

斯威夫特有:

强引用

弱引用

无主引用

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

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

无主引用在 C/C++ 中是否存在类似 dangling pointers 的安全风险?

关于andrewcbancroft.com/2015/05/08/…的非常好的文章
我的经验是对我们控制的类使用 unowned,对于 Apple 类,使用 weak,因为我们无法确定它的作用
@NoorAli,或作为“无主”引用的“ownedBy”通常指向所有者。
注意:对于这些参考中的每一个,都需要注意重要的性能影响:stackoverflow.com/questions/58635303/…
@EpicByte 有时像 Java 或 C# 这样的完整 GC 是值得的。

A
Ayova

weakunowned 引用都不会在引用的对象上创建 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

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 的属性有可能导致强引用循环。这种情况最好使用无主引用来解决。

苹果的注意事项:

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

还有第三种情况,两个属性都应该总是有一个值,一旦初始化完成,两个属性都不应该为 nil。

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

为此,我鼓励您访问 Apple docs,或阅读 the book


这有点微不足道,但我发现 Apartment 和 Person 的示例有点令人困惑,这也提供了打破强引用循环的额外解决方案。一个人的公寓是可选的,因此可以为零,而公寓的租户是可选的,因此可以为零,因此这两个属性都可以定义为弱。 ```
class Person { let name: String init(name: String) { self.name = name } 弱变量公寓:公寓? } class Apartment { let number: Int init(number: Int) { self.number = number } weak var tenant: Person? }
weak var Person?var Person? 有什么区别?
@JustinLevi,如果您将这两个属性都声明为弱,则它们有可能被释放。 Person 保持对公寓的强烈引用,因此公寓不会被释放。如果公寓对 Person 具有相同的强引用,他们将创建一个保留循环 - 如果程序员知道它可以在运行时打破它,否则它只是一个内存泄漏。这都是关于强、弱和无主的大惊小怪:更高级别的内存管理,因为 ARC 为我们做了所有肮脏的事情。避免保留周期是我们的工作。
无主对弱的唯一好处是你不需要解包并且可以使用常量吗?有没有不能使用弱而只能使用无主的实例?
V
Viktor Kucera

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

弱参考:

弱引用是一种不会对其引用的实例保持强控制的引用,因此不会阻止 ARC 处理引用的实例。因为弱引用被允许“没有值”,所以你必须将每个弱引用声明为具有可选类型。 (苹果文档)

无主参考:

与弱引用一样,无主引用不会对其引用的实例保持强控制。然而,与弱引用不同的是,无主引用被假定为始终具有值。因此,无主引用总是被定义为非可选类型。 (苹果文档)

何时使用每个:

只要该引用在其生命周期中的某个时间点变为 nil 有效,就使用弱引用。相反,当您知道一旦在初始化期间设置引用将永远不会为零时,请使用无主引用。 (苹果文档)

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

如上所述,假定无主引用始终具有值。因此,只有在确定引用永远不会为零时,才应该使用它。 Apple Docs 通过以下示例说明了无主引用的用例。

假设我们有两个类 CustomerCreditCard。客户可以没有信用卡而存在,但信用卡没有客户就不会存在,即可以假设信用卡总是有客户。因此,它们应该具有以下关系:

class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

Q3。 “无主引用”是否引用了 C/C++ 中的“悬空指针”之类的安全风险

我不这么认为。

由于无主引用只是保证具有值的弱引用,因此它不应该以任何方式构成安全风险。但是,如果在释放它引用的实例后尝试访问无主引用,则会触发运行时错误,并且应用程序将崩溃。

这是我看到的唯一风险。

Link to Apple Docs


你的 Q2 示例程序很容易理解 unowned..thanks.. 你可以为弱和强添加相同类型的示例吗..
你能举一个无主或弱的常见例子吗?
考虑对象 parent &孩子,如果没有父母就不能存在孩子,那么在孩子类中使用 unowned 作为父母的属性。弱反之亦然。很好的解释@myxtic! unowned 引用只是 weak 保证具有价值的引用!
T
TenaciousJay

如果 self 在闭包中可以为零,请使用 [weak self]。

如果 self 在闭包中永远不会为零,请使用 [unowned self]。

如果在您使用 [unowned self] 时它崩溃了,那么 self 在该闭包中的某个时刻可能为零,您可能需要使用 [weak self] 来代替。

查看在闭包中使用强、弱和无主的示例:

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


即使 self 永远不会为零,为什么不直接使用 weak 呢?
嗨@Boon - 这确实是关键问题。
[弱自我] =>如果我在 viewDidLoad() 中使用闭包,self 怎么可能为零?
@HassanTareq,我认为上面提到的文章中提到了几个很好的例子。检查“解决闭包的强引用循环”部分,尤其是。引用:“ Swift 要求您在闭包中引用 self 的成员时编写 self.someProperty 或 self.someMethod()(而不仅仅是 someProperty 或 someMethod())。这有助于您记住可以通过以下方式捕获 self事故。”摘自:Apple Inc. “Swift 编程语言 (Swift 4)”。 iBooks。itunes.apple.com/de/book/the-swift-programming-language-swift-4/…"
@Boon 如果您总是使用弱,编译器将在使用前强制检查可选。如果您没有进行该检查,则会出现编译时错误。没有其他危害。
B
BangOperator

link 的摘录

几点总结

要确定您是否需要担心强、弱或无主,请询问“我在处理引用类型吗”。如果您使用 Structs 或 Enums,ARC 不会管理这些类型的内存,您甚至无需担心为这些常量或变量指定弱或无主。

强引用在父引用子的层次关系中很好,反之则不行。事实上,强引用在大多数情况下是最合适的引用。

当两个实例可选地相互关联时,请确保其中一个实例持有对另一个实例的弱引用。

当两个实例以这样的方式相关时,其中一个实例没有另一个就不能存在,具有强制依赖关系的实例需要持有对另一个实例的无主引用。


A
Adam Różyński

weakunowned 引用都不会影响对象的引用计数。但是弱引用总是可选的,即它可以是 nil,而 unowned 引用永远不能是 nil,所以它们永远不会是可选的。使用可选引用时,您将始终必须处理对象为零的可能性。如果是无主引用,您必须确保该对象永远不会为零。使用对 nil 对象的无主引用类似于强制解包 nil 的可选项。

也就是说,在您确定对象的生命周期超过引用的生命周期的情况下,使用无主引用是安全的。如果不是这种情况,最好改用弱引用。

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

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

有很多文章更详细地讨论了快速内存管理。 Here 是一。


J
JuJoDi

无主引用是一种弱引用,用于两个对象之间的 Same-Lifetime 关系,此时一个对象只能由另一个对象拥有。这是一种在对象与其属性之一之间创建不可变绑定的方法。

在中级 Swift WWDC 视频中给出的示例中,一个人拥有一张信用卡,而一张信用卡只能有一个持有人。在信用卡上,人不应该是可选的财产,因为你不希望信用卡只有一个所有者。您可以通过将贷方上的持有者属性设为弱引用来打破这个循环,但这也需要您将其设为可选和可变(而不是常量)。在这种情况下,无主引用意味着尽管 CreditCard 在 Person 中没有所有权,但它的生命取决于它。

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

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

链接到 wwdc 视频或标题?
J
J. Doe

当您确定在您访问 selfself 永远不会是 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()
    }
}

如果在您访问 selfself 可能是 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 上询问您在这种情况下应该做什么!在你不应该的时候一直使用弱只会让你和你的代码的读者感到困惑。