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


240

Свифт имеет:

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

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

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

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


3
Очень хорошая статья на andrewcbancroft.com/2015/05/08/…
Зеешан

Мой опыт состоит в том, чтобы использовать unownedдля классов, которые мы контролируем, для классов Apple, weakпотому что мы не можем точно гарантировать, что он делает
onmyway133

@NoorAli, или «ownBy» в качестве ссылки «unowned», часто указывает на владельца.
Ян Рингроз

1
ПРИМЕЧАНИЕ. С каждой из этих ссылок следует иметь в виду важные характеристики производительности: stackoverflow.com/questions/58635303/…
Epic Byte

@EpicByte Иногда полный GC, такой как Java или C #, стоит накладных расходов.
Ян Рингроз

Ответы:


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 }
}

В этом примере a Customerможет иметь или не иметь a CreditCard, но a CreditCard всегда будет связано с a Customer. Чтобы представить это, у Customerкласса есть необязательное cardсвойство, но у CreditCardкласса есть необязательное (и не принадлежащее) customerсвойство.

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

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

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

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

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

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

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


3
Это несколько тривиально, но я нахожу пример «Квартиры и человека» несколько запутанным, что также представляет собой дополнительное решение, позволяющее разорвать цикл строгих ссылок. Квартира человека является необязательной и, следовательно, может быть нулевой, а арендатор квартиры необязательным и, следовательно, может быть нулевым, поэтому оба свойства могут быть определены как слабые. `` `
Джастин Леви Зима

класс Person {пусть name: String init (name: String) {self.name = name} слабый var apartment: Квартира? } квартира класса {пусть номер: Int init (номер: Int) {self.number = номер} слабый var tenant: Person? }
Джастин Леви Зима

3
В чем разница между weak var Person?против var Person??
Декан

4
@JustinLevi, если вы объявите оба свойства как слабые, существует возможность их освобождения. Человек строго ссылается на квартиру, поэтому квартира не будет освобождена. Если квартира будет иметь такую ​​же сильную ссылку на человека, они создадут цикл сохранения - который может быть нарушен программистом во время выполнения, если он знает об этом, но в противном случае это просто утечка памяти. Это все суета о сильном, слабом и непричастном: управление памятью на более высоком уровне, потому что ARC делает всю грязную работу за нас. Избегать сохранения циклов - наша работа.
Илья Кристиан,

1
Неужели единственное преимущество неимущих над слабыми заключается в том, что вам не нужно разворачивать и использовать константу? Есть ли какой-нибудь случай, когда вы не могли использовать слабый, а могли использовать только неподобающий?
Алан

29

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


ваша программа примера Q2 проста для понимания о непричастных .. спасибо .. можете ли вы добавить пример того же типа для слабых и сильных ..
Ранджит Кумар

Превосходно. Спасибо.
Swifty McSwifterton

Можете ли вы привести общий пример для незнакомых или слабых?
Мед

Рассмотрим объекты parent & child, если child не может существовать без родителя, тогда используйте unownedдля свойства parent в дочернем классе. слабый наоборот. Хорошее объяснение @myxtic! unownedссылки - это просто weakссылки, которые гарантированно имеют значение!
Saif

26

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

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

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

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

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


7
Почему бы просто не использовать слабость, даже если самооценка никогда не может быть нулевой, без вреда, сделанного правильно?
Благо

4
Привет @Boon - это действительно важный вопрос.
Толстяк

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

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

1
@Boon Если вы всегда используете слабый, компилятор заставит проверять наличие необязательного перед использованием. Если вы не поставили эту проверку, она выдаст ошибку времени компиляции. Нет другого вреда.
Викас Мишра

5

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

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

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

1

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

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

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

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

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


0

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

В примере, приведенном в промежуточном быстром видео 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.