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

Swift имеет:

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

Чем незарегистрированная ссылка отличается от слабой ссылки?

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

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


person Ian Ringrose    schedule 03.06.2014    source источник
comment
Очень хорошая статья о andrewcbancroft.com/2015/05/08/   -  person Zeeshan    schedule 10.05.2015
comment
По моему опыту я использую unowned для контролируемых нами классов, для классов Apple использую weak, потому что мы не можем точно гарантировать, что он делает.   -  person onmyway133    schedule 20.10.2015
comment
@NoorAli, или ownBy, поскольку безымянная ссылка часто указывает на владельца.   -  person Ian Ringrose    schedule 25.09.2016
comment
ПРИМЕЧАНИЕ. Каждая из этих ссылок имеет важные последствия для производительности: stackoverflow.com/questions/58635303/   -  person Epic Byte    schedule 27.06.2020
comment
@EpicByte Иногда полный GC, такой как Java или C #, стоит накладных расходов.   -  person Ian Ringrose    schedule 27.06.2020
comment
@EpicByte stackoverflow.com/questions/34843124/, это говорит о другом   -  person amar    schedule 27.08.2020


Ответы (7)


Обе ссылки 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 или прочитайте книгу .

person Ilea Cristian    schedule 24.09.2014
comment
Это несколько тривиально, но я нахожу пример с квартирой и персоной несколько сбивающим с толку, который также представляет собой дополнительное решение для разрыва цикла сильных ссылок. Квартира человека не является обязательной и, следовательно, может быть нулевой, а также арендатор квартиры не является обязательной и, следовательно, может быть нулевой, поэтому оба свойства могут быть определены как слабые. `` '' - person Justin Levi Winter; 22.11.2014
comment
class Person {let name: String init (name: String) {self.name = name} weak var apartment: Apartment? } class Apartment {let number: Int init (number: Int) {self.number = number} слабый var tenant: Person? } - person Justin Levi Winter; 22.11.2014
comment
В чем разница между weak var Person? и var Person?? - person Dean; 30.11.2014
comment
@JustinLevi, если вы объявите оба свойства как слабые, есть вероятность, что они будут освобождены. Лицо строго следит за квартирой, поэтому квартира не будет передана. Если бы квартира имела такую ​​же сильную ссылку на человека, они бы создали цикл сохранения, который может быть прерван программистом во время выполнения, если он знает об этом, но в противном случае это просто утечка памяти. Это все суета о сильных, слабых и никому не принадлежащих: управлении памятью на более высоком уровне, потому что ARC делает все грязные вещи за нас. Наша работа - избегать циклов сохранения. - person Ilea Cristian; 30.11.2014
comment
@Ilea Cristian Время жизни weak больше unowned или наоборот? - person bibscy; 24.12.2016
comment
хотя почему это называется бесхозным - person pete; 11.07.2017
comment
Единственное преимущество unowned перед weak в том, что вам не нужно разворачивать и можно использовать константу? Есть ли какой-нибудь случай, когда вы не могли бы использовать слабые и могли использовать только unowned? - person Alan; 27.02.2018
comment
@bibscy Объект, имеющий ссылку на свойство без владельца, всегда действителен. weak свойства могут быть равны нулю в любое время. Итак, я думаю, вы можете сказать, что у unowned ссылки более гарантированный срок службы. - person Alan; 27.02.2018
comment
Итак, weak! то же самое, что unowned, за исключением того, что weak должен быть переменной? - person George_E; 20.05.2019

Q1. Чем «Бесхозная ссылка» отличается от «Слабой ссылки»?

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

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

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

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

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

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


Q2. Когда безопасно использовать «отпечаток без владельца»?

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

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

class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

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

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

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

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

Ссылка на Apple Docs

person Myxtic    schedule 21.10.2014
comment
ваш пример программы Q2, простой для понимания о unowned .. спасибо .. можете ли вы добавить такой же тип примера для слабого и сильного .. - person Ranjith Kumar; 27.03.2015
comment
Можете ли вы привести общий пример для бездомных или слабых? - person Honey; 11.11.2016
comment
Рассмотрим объекты родительский и дочерний, если дочерний элемент не может существовать без родителя, тогда используйте unowned для свойства родителя в дочернем классе. слабый - наоборот. Хорошее объяснение @myxtic! unowned ссылки - это просто weak ссылки, которые гарантированно имеют значение! - person Saif; 05.05.2017

Если 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

person TenaciousJay    schedule 27.10.2014
comment
Почему бы просто не использовать слабое, даже если «я» никогда не может быть нулевым, и никакого вреда не будет нанесено правильно? - person Boon; 25.11.2016
comment
привет @Boon - это действительно важный вопрос. - person Fattie; 24.01.2017
comment
[weak self] = ›Если я использую закрытие внутри viewDidLoad (), как self может быть нулевым? - person Hassan Tareq; 26.09.2017
comment
@HassanTareq, я думаю, пара хороших примеров приведена в упомянутой выше статье. Проверьте раздел «Разрешение циклов сильных ссылок для замыканий», особенно. Цитата: Swift требует, чтобы вы писали self.someProperty или self.someMethod () (а не просто someProperty или someMethod ()) всякий раз, когда вы ссылаетесь на член self внутри замыкания. Это поможет вам помнить, что можно случайно запечатлеть себя ". Отрывок из: Apple Inc. «Язык программирования Swift (Swift 4)». iBooks. itunes.apple. com / de / book / the-swift-programming-language-swift-4 / - person Nick Entin; 21.11.2017
comment
@HassanTareq к моменту выполнения закрытия, которое может быть долгим в будущем (например, http-запрос), пользователь, возможно, уже уволил контроллер представления. Если вы используете unowned, в этом случае приложение выйдет из строя. - person Rafael Nobre; 21.03.2018
comment
@Boon Если вы всегда используете weak, компилятор заставит проверять необязательность перед использованием. Если вы не поставили эту проверку, она выдаст ошибку времени компиляции. Другого Вреда нет. - person Vikas Mishra; 29.05.2019
comment
@Boon Если вы используете weak, переменная будет необязательной, поэтому вам нужно безопасно разворачивать ее каждый раз. Используя unowned, вы можете избежать необходимости делать переменную необязательной, поэтому разворачивайте. Также вы помогаете компилятору, гарантируя, что эта переменная никогда не будет равна нулю. - person Abhijith; 16.09.2019

Выдержки из ссылки < / а>

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

  • Чтобы определить, нужно ли вам вообще беспокоиться о сильных, слабых или незаслуженных, спросите: «Имею ли я дело со ссылочными типами». Если вы работаете со структурами или перечислениями, ARC не управляет памятью для этих типов, и вам даже не нужно беспокоиться об указании weak или unowned для этих констант или переменных.
  • Сильные ссылки хороши в иерархических отношениях, когда родитель ссылается на дочерний элемент, но не наоборот. Фактически, сильные ссылки в большинстве случаев являются наиболее подходящими.
  • Если два экземпляра необязательно связаны друг с другом, убедитесь, что один из этих экземпляров содержит слабую ссылку на другой.
  • Когда два экземпляра связаны таким образом, что один из экземпляров не может существовать без другого, экземпляр с обязательной зависимостью должен содержать не принадлежащую ему ссылку на другой экземпляр.
person Abhinav Singh    schedule 21.05.2015

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

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

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

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

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

person Deeksha Kaul    schedule 05.10.2019

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

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

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

    init (holder: Person) {
        self.holder = holder
    }
}
person JuJoDi    schedule 06.06.2014
comment
ссылка на wwdc видео или заголовок? - person Osa; 18.02.2019

Используйте 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, что вам следует делать в вашем случае! Постоянное использование weak вместо этого просто сбивает с толку вас и читателя вашего кода.

person J. Doe    schedule 26.12.2018