簡體   English   中英

弱引用和無主引用有什么區別?

[英]What is the difference between a weak reference and an unowned reference?

斯威夫特有:

  • 強引用
  • 弱引用
  • 無主引用

無主引用與弱引用有何不同?

什么時候使用無主引用是安全的?

無主引用是否存在安全風險,例如 C/C++ 中的懸空指針

weak引用和unowned引用都不會對引用的對象創建strong保留(也就是它們不會增加保留計數以防止 ARC 釋放引用的對象)。

但是為什么是兩個關鍵字呢? 這種區別與Optional類型內置於 Swift 語言這一事實​​有關。 長話短說:可選類型提供內存安全(這與Swift 的構造函數規則完美配合——為了提供這種好處而嚴格)。

weak引用允許它變為nil的可能性(當被引用的對象被釋放時會自動發生),因此你的屬性的類型必須是可選的 - 所以作為程序員,你有義務在使用它之前檢查它(基本上編譯器會強迫您盡可能多地編寫安全代碼)。

unowned引用假定它在其生命周期內永遠不會變為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

Customer and CreditCard示例顯示了一種情況,其中一個屬性允許為 nil,另一個屬性不能為 nil 有可能導致強引用循環。 這種情況最好使用無主引用來解決。

蘋果的注意事項:

弱引用必須聲明為變量,以表明它們的值可以在運行時改變。 弱引用不能聲明為常量。

還有第三種情況,即兩個屬性都應該始終有一個值,並且一旦初始化完成,兩個屬性都不應該為零。

在使用閉包時,還有一些經典的保留循環場景需要避免。

為此,我鼓勵您訪問Apple 文檔或閱讀本書

一季度。 “無主引用”與“弱引用”有何不同?

弱參考:

弱引用是一種引用,它不會對其引用的實例保持強控制,因此不會阻止 ARC 處理引用的實例。 因為弱引用被允許“沒有值”,所以你必須將每個弱引用聲明為具有可選類型。 (蘋果文檔)

無主參考:

與弱引用一樣,無主引用不會對其引用的實例保持強控制。 然而,與弱引用不同的是,無主引用被假定為始終具有值。 因此,無主引用始終定義為非可選類型。 (蘋果文檔)

何時使用:

只要該引用在其生命周期中的某個時刻變為 nil 是有效的,就使用弱引用。 相反,當您知道一旦在初始化期間設置引用將永遠不會為零時,請使用無主引用。 (蘋果文檔)


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

如上所述,假定無主引用始終具有值。 所以你應該只在你確定引用永遠不會為零時使用它。 Apple Docs 通過以下示例說明了無主引用的用例。

假設我們有兩個類CustomerCreditCard 沒有信用卡客戶可以存在,但沒有客戶信用卡將不存在,即可以假設信用卡總是有客戶。 所以,他們應該有以下關系:

class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

Q3。 “無主引用”是否引用了像 C/C++ 中的“懸空指針”這樣的安全風險

我不這么認為。

由於無主引用只是保證具有值的弱引用,因此它不應該以任何方式構成安全風險。 但是,如果在釋放引用的實例后嘗試訪問無主引用,則會觸發運行時錯誤,並且應用程序將崩潰。

這是我看到的唯一風險。

鏈接到 Apple 文檔

如果self在閉包中可以為零,請使用[weak self]

如果self在閉包中永遠不會為零,請使用[unowned self]

如果它在您使用[unowned self]時崩潰,那么 self 在該閉包中的某個時刻可能為零,您可能需要使用[weak self]來代替。

查看在閉包中使用strongweakunowned的例子:

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

摘自鏈接

幾個結論

  • 要確定您是否需要擔心強、弱或無主,請詢問“我在處理引用類型嗎”。 如果您正在使用結構或枚舉,ARC 不會管理這些類型的內存,您甚至不必擔心為這些常量或變量指定弱或無主。
  • 強引用在父引用子的層次關系中很好,但反之則不然。 事實上,大多數時候,強引用是最合適的引用類型。
  • 當兩個實例可選地相互關聯時,請確保其中一個實例對另一個實例持有弱引用。
  • 當兩個實例以這樣一種方式相關時,一個實例不能沒有另一個實例而存在,具有強制依賴關系的實例需要持有對另一個實例的無主引用。

weak引用和unowned引用都不會影響對象的引用計數。 但是弱引用始終是可選的,即它可以為零,而無unowned引用永遠不會為零,因此它們永遠不會是可選的。 使用可選引用時,您將始終必須處理對象為零的可能性。 在無主引用的情況下,您必須確保該對象永遠不會為零。 使用對 nil 對象的無主引用類似於強制解包 nil 的可選項。

也就是說,在您確定對象的生命周期大於引用的生命周期的情況下,使用無主引用是安全的。 如果不是這種情況,最好改用弱引用。

至於問題的第三部分,我不認為無主引用類似於懸空指針。 當我們談論引用計數時,我們通常指的是對象的強引用計數。 類似地,swift 維護對象的無主引用計數和弱引用計數(弱引用指向稱為“邊桌”的東西,而不是對象本身)。 當強引用計數達到零時,對象將被取消初始化,但如果無主引用計數大於零,則無法解除分配。

現在懸空指針指向一個已經被釋放的內存位置。 但是在 swift 中,因為只有存在對對象的無主引用才能釋放內存,所以它不會導致懸空指針。

有很多文章更詳細地討論了 swift 內存管理。 是一個。

無主引用是一種弱引用,用於兩個對象之間存在相同生命周期關系的情況下,當一個對象應該只由另一個對象擁有時。 這是一種在對象與其屬性之一之間創建不可變綁定的方法。

在中間 swift WWDC 視頻中給出的示例中,一個人擁有一張信用卡,而一張信用卡只能有一個持有人。 在信用卡上,該人不應該是可選財產,因為您不希望信用卡只有一個所有者。 您可以通過將信用上的 holder 屬性設置為弱引用來打破這個循環,但這也要求您將其設置為可選和可變(而不是常量)。 在這種情況下,無主引用意味着盡管 CreditCard 不擁有 Person 的所有權,但它的生命取決於它。

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

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

ARC是一個編譯時功能,是Apple的自動內存管理版本。 它代表自動參考計數。 這意味着當對象沒有引用時, 它只釋放對象的內存。

強大

它本質上是一個普通的引用(指針和全部),但它本身特別之處在於它通過將保留計數增加1來保護被引用對象不被ARC解除分配。本質上, 只要任何東西都有強引用一個對象,它不會被釋放。

通常,當對象的層次關系是線性的時,我們可以安全地使用強引用。 當強引用的層次結構從父級傳遞到子級時,使用強引用總是可以的。

解決類實例之間的強引用周期

在有潛在保留周期的情況下,使用weak變量和unowned變量的重要位置。 當兩個對象都具有相互強引用時,會發生保留周期。 如果2個對象彼此具有強引用,則ARC將不會在每個實例上生成相應的釋放消息代碼,因為它們彼此保持活動狀態。

弱引用只是指向對象的指針,該對象不保護對象不被ARC釋放。 雖然強引用會將對象的保留計數增加1,但弱引用則不會 此外,弱引用在成功解除分配時將指針歸零。 這可以確保在訪問弱引用時,它將是有效對象,或者為nil

當引用它的實例被解除分配時,ARC會自動將弱引用設置為nil。 並且,因為弱引用需要允許它們的值在運行時更改為nil ,所以它們總是被聲明為變量

運用

在兩種屬性的情況下,兩種屬性都允許nil 使用weak引用可以最好地解決此方案。

當另一個實例的生命周期更短時 ,即在可以首先釋放另一個實例時,使用weak引用。

無主

無主引用(如弱引用)不會增加被引用對象的保留計數。 無主引用是非歸零的。 這意味着當對象被釋放時,它不會將指針清零。 這意味着在某些情況下,使用無主引用會導致懸空指針

ARC從未將無主引用的值設置為nil,這意味着使用非可選類型定義無主引用。


重要。

僅當您確定引用始終引用尚未釋放的實例時,才使用無主引用。

如果在釋放該實例后嘗試訪問無主引用的值,則會出現運行時錯誤 - Attempted to read an unowned reference but object was already deallocated


注意

對於需要禁用運行時安全檢查的情況,Swift還提供不安全的無主參考 - 例如,出於性能原因。 與所有不安全的操作一樣,您負責檢查該代碼的安全性。

您通過編寫unowned(unsafe)表示不安全的無主參考。 如果在取消分配引用的實例后嘗試訪問不安全的無主引用,則程序將嘗試訪問實例所在的內存位置,這是一種不安全的操作。

運用

在的情況下被允許將一個屬性nil ,並且不能是另一個屬性nil 使用unowned引用可以最好地解決此方案。

當另一個實例具有相同的生命周期或更長的生命周期時,請使用unowned引用。 就像一個隱式展開的可選項 ,如果你可以保證引用在它的使用點不會nil ,那就使用unowned 如果沒有,那么你應該使用weak

強烈建議閱讀文檔來源

當您確定在訪問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 上問你應該怎么做! 在您不應該使用的時候一直使用 weak 只會讓您和您的代碼讀者感到困惑。

摘自喬恩·霍夫曼(Jon Hoffman)的書“精通Swift 4”:

弱引用無主的參考值之間的差別在於,其弱引用是指實例可以是零,而實例無主的參考指的是不能為零。 這意味着當我們使用弱引用時 ,該屬性必須是可選屬性 ,因為它可以為nil。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM