Яка різниця між слабкою посиланням і невідомою посиланням?


240

Свіфт має:

  • Сильні посилання
  • Слабка література
  • Невідомі посилання

Чим невідома посилання відрізняється від слабкої посилання?

Коли безпечно використовувати невідому посилання?

Чи невідомі посилання є ризиком безпеки, як звисаючі покажчики на C / C ++?


3
Дуже хороша стаття на andrewcbancroft.com/2015/05/08 / ...
Zeeshan

Мій досвід полягає у використанні unownedдля класів, якими ми weak
керуємо

@NoorAli, або "lastBy", як "невідома" посилання часто вказує на власника.
Ян Рінроуз

Відповіді:


361

І посилання, weakі unownedпосилання не створюють strongзатримку на згаданому об'єкті (він також не збільшує кількість утримування, щоб не допустити ARC переходити згаданий об'єкт).

Але чому два ключові слова? Ця відмінність пов'язана з тим, що Optionalтипи вбудовані в мову Swift. Довга коротка розповідь про них: опціональні типи забезпечують безпеку пам’яті (це чудово працює з правилами конструктора Swift - які суворі для того, щоб забезпечити цю перевагу).

weakПосилання допускає можливість його стати nil(це відбувається автоматично , коли об'єкт посилання звільняється), тому тип вашої власності повинен бути обов'язково - так що ви, як програміст, зобов'язані перевірити його , перш ніж використовувати його ( в основному компілятор змушує вас, наскільки це можливо, написати безпечний код).

An unownedпосилання передбачає , що він ніколи не стане в nilпротягом його життя. Під час ініціалізації необхідно встановити невідоме посилання - це означає, що посилання буде визначено як необов'язковий тип, який можна безпечно використовувати без перевірок. Якщо якимось чином об'єкт, на який йдеться, буде розміщений, тоді додаток вийде з ладу, коли буде використана невідома посилання.

З документів Apple :

Використовуйте слабку посилання, коли це дійсно, щоб ця посилання стала нульовою в якийсь момент її життя. І навпаки, використовуйте невідому посилання, коли знаєте, що посилання ніколи не буде нульовою після того, як вона була встановлена ​​під час ініціалізації.

У документах є кілька прикладів, які обговорюють утримувані цикли та способи їх розбиття. Усі ці приклади витягнуті з документів .

Приклад 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і Apartmentприклад показує ситуацію, коли дві властивості, обом з яких дозволено бути нульовими, можуть викликати сильний еталонний цикл. Цей сценарій найкраще вирішити зі слабким посиланням. Обидва суб'єкти можуть існувати без жорсткої залежності від інших.

Приклад 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 завжди буде пов'язано з a Customer. Щоб представити це, Customerклас має необов'язкове cardвластивість, але CreditCardклас має необов'язкове (і невідоме) customerвластивість.

Customer ===(strong)==> CreditCard
Customer <==(unowned)== CreditCard

Приклад Customerі CreditCardприклад показує ситуацію, коли одна властивість, якій дозволено бути нульовою, а інша властивість, яка не може бути нульовою, може викликати сильний еталонний цикл. Цей сценарій найкраще вирішити за допомогою невідомої посилання.

Примітка від Apple:

Слабкі посилання повинні бути оголошені змінними, щоб вказати, що їх значення може змінюватися під час виконання. Слабка посилання не може бути оголошена постійною.

Існує також третій сценарій, коли обидва властивості завжди повинні мати значення, і жодна властивість ніколи не повинна бути нульовою після завершення ініціалізації.

Є також класичні сценарії збереження циклу, яких слід уникати при роботі із закриттями.

Для цього я закликаю вас відвідати документи Apple або прочитати книгу .


3
Це дещо тривіально, але я вважаю, що приклад квартири та особи є дещо заплутаним, що також є додатковим рішенням для розірвання сильного еталонного циклу. Квартира людини не є обов'язковою, тому вона може бути нульовою, а також орендар квартири не є обов'язковим, а тому може бути нульовим, тому обидва властивості можна визначити як слабкі. `` `
Джастін Леві Зима

Особа класу {хай ім'я: String init (ім'я: String) {self.name = name} слабка вар квартира: Квартира? } Квартира класу {нехай номер: Int init (кількість: Int) {self.number = число} слабкий орендар: Особа? }
Джастін Леві Зима

3
У чому різниця між weak var Person?VS. var Person??
Декан

4
@JustinLevi, Якщо ви оголосите обидві властивості слабкими, є можливість їх розміщення. Людина чітко посилається на Квартиру, щоб Квартира не була розміщена. Якщо квартира мала б однакову орієнтацію на Особу, вони створили б цикл утримування - який програміст може порушити під час виконання, якщо він про це знає, але в іншому випадку це лише витік пам'яті. Це вся суєта з приводу сильних, слабких і невідомих: управління пам’яттю на більш високому рівні, адже ARC робить все брудне для нас. Уникати циклів утримування - наша робота.
Ілеа Крістіан

1
Єдиною перевагою невідомого над слабким є те, що вам не потрібно розгортатись та можете користуватися постійною? Чи є якийсь приклад, де ви не могли використовувати слабких і могли використовувати лише невідомі?
Алан

29

Q1. Чим "Невідомий довідник" відрізняється від "Слабого посилання"?

Слабка довідка:

Слабка посилання - це посилання, яке не тримає сильної точки зору на екземпляр, на який він посилається, і тому не перешкоджає ARC розпоряджатися посиланням на цей екземпляр. Оскільки для слабких посилань дозволено не мати "значення", ви повинні оголосити кожну слабку посилання як необов'язковий тип. (Документи Apple)

Невідома довідка:

Як і слабкі посилання, невідома посилання не тримає сильної позиції щодо екземпляра, на який вона посилається. На відміну від слабкої посилання, однак, невідома посилання завжди має значення. Через це невідоме посилання завжди визначається як необов'язковий тип. (Документи Apple)

Коли використовувати кожен:

Використовуйте слабку посилання, коли це дійсно, щоб ця посилання стала нульовою в якийсь момент її життя. І навпаки, використовуйте невідому посилання, коли знаєте, що посилання ніколи не буде нульовою після того, як вона була встановлена ​​під час ініціалізації. (Документи Apple)


Q2. Коли безпечно використовувати «невідому посилання»?

Як було сказано вище, невідома посилання завжди має значення. Тож слід використовувати його лише тоді, коли ви впевнені, що посилання ніколи не буде нульовим. Документи Apple ілюструють випадок використання для невідомих посилань на наступному прикладі.

Припустимо, у нас є два класи Customerта CreditCard. Клієнт може існувати без кредитної картки, але кредитна картка не існуватиме без клієнта, тобто можна припустити, що кредитна картка завжди матиме клієнта. Отже, вони повинні мати такі відносини:

class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

Q3. Чи "невідомий посилання" посилається на такий ризик безпеки, як "звисаючі покажчики" в C / C ++

Я не думаю, що так.

Оскільки невідомі посилання є лише слабкими посиланнями, які гарантовано мають значення, це ні в якому разі не повинно бути ризиком для безпеки. Однак якщо ви спробуєте отримати доступ до невідомої посилання після того, як екземпляр, на який вона посилається, розміщений, ви запустить помилку виконання, і додаток вийде з ладу.

Це єдиний ризик, який я бачу з цим.

Посилання на Apple Docs


ваша програма для прикладу Q2 проста, щоб зрозуміти про невідомі .. дякую .. Ви можете додати той же тип прикладу для слабких і сильних ..
Ranjith Kumar

Відмінно. Дякую.
Swifty McSwifterton

Чи можете ви включити загальний приклад для невідомих або слабких?
Мед

Розглянемо об'єкти батько та дитина, якщо дитина не може існувати без батьків, тоді використовуйте unownedдля власності батьків у дочірньому класі. слабкий - навпаки. Приємне пояснення @myxtic! unownedпосилання - це лише weakпосилання, які гарантовано мають значення!
Сайф

26

Якщо самості може бути нульовим у закритті, використовуйте [слабке " .

Якщо самості ніколи не буде нульовим у закритті, використовуйте [невідомий я] .

Якщо він виходить з ладу, коли ви використовуєте [невідому себе], то, ймовірно, "я" є нульовим в певний момент цього закриття, і вам, ймовірно, потрібно буде використовувати [слабке "] .

Перегляньте приклади використання сильних , слабких та невідомих у закриттях:

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


7
Чому б просто не використати слабкого, навіть якщо самоврядування ніколи не може бути нульовим, не завдавши шкоди правильно?
Бун

4
привіт @Boon - це справді критичне питання.
Fattie

[слабкий "] => Якщо я використовую закриття всередині viewDidLoad (), як можна selfзробити нуль?
Хассан Тарек

@HassanTareq, я думаю, що в статті, згаданій вище, згадується кілька хороших прикладів. Перевірте розділ «Розв’язання сильних еталонних циклів для закриттів», esp. Цитата: "Swift вимагає, щоб ви писали self.someProperty або self.someMethod () (а не просто someProperty чи someMethod ()) щоразу, коли ви звертаєтесь до члена self в межах закриття. Це допомагає вам пам’ятати, що можна зробити самозахист за допомогою аварія. " Уривок: Apple Inc. "Мова швидкого програмування (Swift 4)". iBooks. itunes.apple.com/de/book/the-swift-programming-language-swift-4/… "
Nick Entin

1
@Boon Якщо ви завжди використовуєте слабкий, компілятор змусить перевірити його на вибір перед використанням. Якщо ви не поставили цю перевірку, вона видасть помилку часу компіляції. Іншої шкоди немає.
Вікас Мішра

5

Виписки з посилання

Кілька підсумкових моментів

  • Щоб визначити, чи потрібно навіть турбуватися про сильних, слабких чи невідомих, запитайте: «Чи маю я справу з референтними типами». Якщо ви працюєте з Structs або Enums, ARC не керує пам'яттю для цих типів, і вам навіть не потрібно турбуватися про те, щоб вказати слабкі або невідомі для цих констант або змінних.
  • Сильні посилання є чіткими в ієрархічних відносинах, де батько посилається на дитину, але не навпаки. Насправді, чіткі посилання є найбільш підходящим видом посилань більшу частину часу.
  • Коли два екземпляри необов'язково пов'язані один з одним, переконайтеся, що один з цих екземплярів має слабке посилання на інший.
  • Коли два екземпляри пов'язані таким чином, що один з екземплярів не може існувати без іншого, екземпляр із обов'язковою залежністю повинен містити невідомий посилання на інший екземпляр.

1

І посилання, weakі unownedпосилання не впливатимуть на кількість посилань об'єкта. Але слабке посилання завжди буде необов’язковим, тобто воно може бути нульовим, тоді якunowned посилання ніколи не можуть бути нульовими, тому вони ніколи не будуть необов'язковими. Під час використання додаткової посилання вам завжди доведеться обробляти можливість об'єкта нульовим. У разі невідомої посилання вам доведеться переконатися, що об’єкт ніколи не буде нульовим. Використання невідомої посилання на об'єкт нуля буде подібним до примусового розгортання необов'язкової, яка є нульовою.

Це означає, що безпечно використовувати невідому посилання, коли ви впевнені, що термін експлуатації об'єкта більший, ніж еталонний. Якщо це не так, краще замість цього використати слабке посилання.

Що стосується третьої частини питання, я не думаю, що невідома посилання схожа на звисаючий покажчик. Коли ми говоримо про підрахунок еталонів, ми зазвичай маємо на увазі сильний довідковий підрахунок об'єкта. Аналогічно, швидкий підтримує невідомий опорний підрахунок і слабкий відлік посилань на об'єкт (слабкі орієнтири на щось, що називається "бічна таблиця", а не сам об'єкт). Коли сильний відліку досягає нуля, об'єкт деніціалізується, але він не може бути розміщений, якщо невідомий контрольний підрахунок більше нуля.

Тепер звисаючий покажчик - це те, що вказує на місце пам'яті, яке вже було розміщено. Але швидко, оскільки пам'ять може бути розміщена лише до тих пір, поки є невідома посилання на об'єкт, це не може викликати звисаючий покажчик.

Існує маса статей, які детальніше обговорюють швидке управління пам'яттю. Ось один.


0

Невідомі посилання є своєрідною слабкою посиланням, що використовується у випадку одночасного відношення між двома об’єктами, коли об’єктом має бути колись належить лише інший об’єкт. Це спосіб створити незмінне зв’язування між об'єктом та одним із його властивостей.

У прикладі, наведеному у проміжному швидкому відео WWDC, особа володіє кредитною карткою, а кредитна картка може мати лише одного власника. На кредитній картці особа не повинна бути власністю, оскільки ви не хочете, щоб кредитна картка плавала лише одним власником. Ви можете порушити цей цикл, зробивши властивість власника кредиту слабкою посиланням, але це також вимагає, щоб ви зробили його необов'язковим, а також змінним (на відміну від постійного). Невідома посилання в цьому випадку означає, що хоча CreditCard не має власного пакету акцій Особи, її життя залежить від неї.

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

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

посилання на відео або назву wwdc?
Оса

-2

Використовуйте, 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:

  • Більш ефективний, ніж слабкий
  • Ви можете (ну, ви змушені) позначити екземпляр як непорушний (вже не з Swift 5.0).
  • Вказує читачеві вашого коду: Цей примірник має відношення до X, і він не може жити без нього, але якщо X зникне, я теж пішов.

Мінуси weak:

  • Більш безпечний, ніж невідомий (оскільки він не може вийти з ладу).
  • Можна створити відносини до X, які йдуть обома способами, але обидва можуть жити один без одного.

Якщо ви не впевнені, використовуйте weak. Зачекайте , я маю на увазі запитати тут на StackOverflow, що вам робити у вашому випадку! Використовувати слабкий весь час, коли не варто, це просто заплутане для вас та читача вашого коду.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.