В чем разница между слабой ссылкой и неподтвержденной ссылкой?

Свифт имеет:

  • Сильные Ссылки
  • Слабые ссылки
  • Неизвестные ссылки

Чем неподходящая ссылка отличается от слабой ссылки?

Когда безопасно использовать неподтвержденную ссылку?

Являются ли неизвестные ссылки угрозой безопасности, такой как висячие указатели в C/C++?

6 ответов

Решение

И то и другое weak а также unowned ссылки не создают strong удерживайте указанный объект (иначе они не увеличивают количество сохраняемых файлов, чтобы предотвратить освобождение ARC указанного объекта).

Но почему два ключевых слова? Это различие связано с тем, что Optional типы встроены в язык Swift. Короткая история о них: необязательные типы обеспечивают безопасность памяти (это прекрасно работает с правилами конструктора Swift - которые являются строгими, чтобы обеспечить это преимущество).

weak ссылка позволяет возможность его стать nil (это происходит автоматически, когда ссылочный объект освобождается), поэтому тип вашего свойства должен быть необязательным - поэтому вы, как программист, обязаны проверять его, прежде чем использовать его (в основном, компилятор заставляет вас, насколько это возможно,, чтобы написать безопасный код).

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 всегда будет связан с Customer, Чтобы представить это, Customer класс имеет необязательный card собственность, но CreditCard класс имеет необязательный (и не принадлежащий) customer имущество.

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

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

Примечание от Apple:

Слабые ссылки должны быть объявлены как переменные, чтобы указать, что их значение может изменяться во время выполнения. Слабая ссылка не может быть объявлена ​​константой.

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

И есть также классические сценарии сохранения цикла, которых следует избегать при работе с замыканиями.

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

Q1. Чем "Неизвестная ссылка" отличается от "Слабой ссылки"?

Слабая ссылка:

Слабая ссылка - это ссылка, которая не удерживает сильную привязку к экземпляру, на который она ссылается, и, таким образом, не мешает ARC избавиться от ссылочного экземпляра. Поскольку слабым ссылкам разрешено иметь "никакого значения", вы должны объявить каждую слабую ссылку как имеющую необязательный тип. (Apple Docs)

Неизвестная ссылка:

Подобно слабым ссылкам, неподдерживаемая ссылка не сохраняет сильного влияния на экземпляр, на который она ссылается. В отличие от слабой ссылки, однако, предполагается, что у неизвестной ссылки всегда есть значение. Из-за этого неопознанная ссылка всегда определяется как необязательный тип. (Apple Docs)

Когда использовать каждый:

Используйте слабую ссылку, когда она действительна, чтобы в какой-то момент времени эта ссылка стала нулевой. И наоборот, используйте неизвестную ссылку, если вы знаете, что ссылка никогда не будет равна нулю, если она была установлена ​​во время инициализации. (Apple Docs)


Q2. Когда безопасно использовать "ссылку без ссылки"?

Как указывалось выше, предполагается, что у неизвестной ссылки всегда есть значение. Поэтому вы должны использовать его только тогда, когда уверены, что ссылка никогда не будет равна нулю. Документы Apple иллюстрируют вариант использования для неиспользованных ссылок в следующем примере.

Предположим, у нас есть два класса Customer а также CreditCard, Клиент может существовать без кредитной карты, но кредитная карта не будет существовать без клиента, то есть можно предположить, что у кредитной карты всегда будет клиент. Итак, они должны иметь следующие отношения:

class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

Q3. Являются ли "неизвестные ссылки" ссылками на угрозу безопасности, такие как "висячие указатели" в C/C++

Я так не думаю.

Поскольку неизвестные ссылки являются просто слабыми ссылками, которые гарантированно имеют значение, это никоим образом не должно представлять угрозу безопасности. Однако, если вы попытаетесь получить доступ к неизвестной ссылке после того, как экземпляр, на который она ссылается, будет освобожден, вы вызовете ошибку времени выполнения, и приложение вылетит.

Это единственный риск, который я вижу с этим.

Ссылка на Apple Docs

Если " я" может быть нулевым в замыкании, используйте " слабое я".

Если self никогда не будет нулевым в закрытии, используйте [unowned self].

Если происходит сбой, когда вы используете [unowned self], тогда self, вероятно, равно нулю в какой-то момент в этом закрытии, и вам, вероятно, нужно вместо этого использовать [слабое self].

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

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

Выдержки из ссылки

Несколько заключительных пунктов

  • Чтобы определить, нужно ли вам беспокоиться о сильных, слабых или не владеющих, спросите: "Имею ли я дело с ссылочными типами". Если вы работаете со структурами или перечислениями, ARC не управляет памятью для этих типов, и вам даже не нужно беспокоиться об указании слабых или неизвестных для этих констант или переменных.
  • Сильные ссылки хороши в иерархических отношениях, где родитель ссылается на дочерний элемент, но не наоборот. На самом деле, сильные ссылки - это наиболее подходящая ссылка в большинстве случаев.
  • Если два экземпляра необязательно связаны друг с другом, убедитесь, что один из этих экземпляров содержит слабую ссылку на другой.
  • Когда два экземпляра связаны таким образом, что один из экземпляров не может существовать без другого, экземпляр с обязательной зависимостью должен содержать ссылку на другой объект без использования.

Обе weak а также unownedссылки не повлияют на счетчик ссылок объекта. Но слабая ссылка всегда будет необязательной, т.е. она может быть нулевой, тогда какunownedссылки никогда не могут быть нулевыми, поэтому они никогда не будут необязательными. При использовании необязательной ссылки вам всегда придется учитывать возможность нулевого значения объекта. В случае незарегистрированной ссылки вам нужно будет убедиться, что объект никогда не равен нулю. Использование незарегистрированной ссылки на объект nil будет аналогично принудительному разворачиванию необязательного значения, равного nil.

Тем не менее, безопасно использовать ссылку без владельца, если вы уверены, что время жизни объекта больше, чем у ссылки. Если это не так, лучше использовать слабую ссылку.

Что касается третьей части вопроса, я не думаю, что ссылка без владельца похожа на висячий указатель. Когда мы говорим о подсчете ссылок, мы обычно имеем в виду сильное количество ссылок на объект. Точно так же swift поддерживает счетчик бесхозных ссылок и слабый счетчик ссылок для объекта (слабые ссылки указывают на нечто, называемое "вспомогательной таблицей", а не на сам объект). Когда счетчик сильных ссылок достигает нуля, объект деинициализируется, но он не может быть освобожден, если счетчик бесхозных ссылок больше нуля.

Теперь висячий указатель - это что-то, что указывает на область памяти, которая уже была освобождена. Но в быстром темпе, поскольку память может быть освобождена только до тех пор, пока существует незарегистрированная ссылка на объект, это не может вызвать висящий указатель.

Есть много статей, в которых более подробно обсуждается быстрое управление памятью. Вот один.

ARC

ARC - это функция времени компиляции, которая является версией Apple для автоматического управления памятью. Это означает автоматический подсчет ссылок. Это означает, что он освобождает память для объектов только тогда, когда на них нет сильных ссылок.

STRONG

По сути, это нормальная ссылка (указатель и все), но она особенная сама по себе в томсмысле, что защищает указанный объект от освобождения ARC за счет увеличения количества сохраняемых данных на 1. По сути, до тех пор, пока что-либо имеет сильную ссылку на объект, он не будет освобожден.

Как правило, мы можем использовать надежные ссылки, когда иерархические отношения объектов линейны. Когда иерархия сильных ссылок переходит от родителя к потомку, всегда можно использовать сильные ссылки.

Устранение сильных ссылочных циклов между экземплярами классов

Важные места для использования weak а также unowned переменные в тех случаях, когда у вас есть потенциальные циклы сохранения. Цикл сохранения - это то, что происходит, когда два объекта имеют сильные ссылки друг на друга. Если 2 объекта имеют сильные ссылки друг на друга, ARC не будет генерировать соответствующий код сообщения об освобождении для каждого экземпляра, поскольку они поддерживают друг друга живыми.

WEAK

Слабая ссылка - это просто указатель на объект, который не защищает объект от освобождения ARC. В то время как сильные ссылки увеличивают количество сохраняемых объектов на 1, слабые ссылки - нет. Кроме того, слабые ссылки обнуляют указатель на ваш объект, когда он успешно освобождается. Это гарантирует, что при доступе к слабой ссылке это будет либо действительный объект, либо nil,

ARC автоматически устанавливает слабую ссылку на ноль, когда экземпляр, на который он ссылается, освобождается. И, поскольку слабые ссылки должны позволять изменять их значение на 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,

Настоятельно рекомендуем прочитать документ и источник

Из книги Джона Хоффмана "Освоение Свифта 4.":

Разница между слабой ссылкой и неподтвержденной ссылкой заключается в том, что экземпляр, на который ссылается слабая ссылка, может быть нулем, тогда как экземпляр, на который ссылается не принадлежащая ссылка, не может быть нулем. Это означает, что когда мы используем слабую ссылку, свойство должно быть необязательным, поскольку оно может быть равно нулю.

Неизвестные ссылки - это своего рода слабая ссылка, используемая в случае отношения Same-Lifetime между двумя объектами, когда объект должен когда-либо принадлежать только одному другому объекту. Это способ создать неизменную связь между объектом и одним из его свойств.

В примере, приведенном в промежуточном быстром видео WWDC, человек владеет кредитной картой, и у кредитной карты может быть только один держатель. На кредитной карте человек не должен быть необязательным свойством, потому что вы не хотите, чтобы у кредитной карты был только один владелец. Вы можете разорвать этот цикл, сделав свойство держателя по кредиту слабой ссылкой, но для этого также необходимо, чтобы оно было необязательным и переменным (в отличие от константы). Неизвестная ссылка в этом случае означает, что, хотя у CreditCard нет собственной доли в Лице, от этого зависит его жизнь.

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

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

Использование 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:

  • Эффективнее слабого
  • Вы можете (ну, вы вынуждены) пометить экземпляр как неизменный.
  • Указывает читателю вашего кода: этот экземпляр имеет отношение к X и не может жить без него, но если X ушел, я тоже ушел.

Минусы weak:

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

Если вы не уверены, используйте weak, Подождите, я имею в виду, спросите здесь, на Stackru, что вы должны делать в вашем случае! Использование слабых все время, когда вы не должны, просто сбивает с толку вас и читателя вашего кода.

Другие вопросы по тегам