слабый против бесхозного в Swift. Каковы внутренние различия?

Я понимаю использование и поверхностные различия между weak и unowned в Swift:

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

С другой стороны, в случае Human и Heart, Heart может иметь ссылку unowned на человека, потому что, как только Human становится... "разыменованным", Heart становится недоступным. Это и классический пример с Customer и CreditCard.

Так что это не дубликат вопросов об этом.


У меня вопрос, какой смысл иметь два таких похожих понятия? Каковы внутренние различия, которые требуют наличия двух ключевых слов для того, что кажется на 99% одним и тем же? Вопрос в том, ПОЧЕМУ существуют различия, а не в чем они заключаются.

Учитывая, что мы можем просто установить такую ​​переменную: weak var customer: Customer!, преимущество необязательных переменных unowned является спорным вопросом.

Единственное практическое преимущество, которое я вижу в использовании unowned по сравнению с неявным развертыванием переменной weak через !, заключается в том, что мы можем сделать unowned ссылок постоянными через let.

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

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

Мне было бы очень интересно услышать от людей, которые работали над компилятором Swift (или другими компиляторами).


person ephemer    schedule 16.03.2017    source источник
comment
Возможный дубликат В чем разница между слабая ссылка и бесхозная ссылка?   -  person Dalija Prasnikar    schedule 16.03.2017
comment
@DalijaPrasnikar Не согласен. ОП спрашивает, чем отличаются реализации; вопрос, который вы связали, касался концептуальных (т.е. использования) различий.   -  person jlehr    schedule 16.03.2017
comment
ПРИМЕЧАНИЕ. Каждая из этих ссылок имеет важные последствия для производительности: stackoverflow.com/questions/58635303/   -  person Epic Byte    schedule 27.06.2020


Ответы (2)


У меня вопрос, какой смысл иметь два таких похожих понятия? Каковы внутренние различия, которые требуют наличия двух ключевых слов для того, что кажется на 99% одним и тем же?

Они совсем не похожи. Они настолько разные, насколько это возможно.

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

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

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

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

По этой причине unowned всегда следует отдавать предпочтение везде, где это применимо. Но никогда не используйте его, если это не абсолютно безопасно! С unowned вы отбрасываете автоматическое управление памятью и безопасность. Вы намеренно возвращаетесь к старым недобрым временам до ARC.

В моем случае общий случай возникает в ситуациях, когда для замыкания требуется список захвата, включающий self, чтобы избежать цикла сохранения. В такой ситуации почти всегда можно сказать [unowned self] в списке захвата. Когда мы делаем:

  • Программисту удобнее, потому что распаковывать нечего. [weak self] будет необязательным, и его нужно развернуть, чтобы использовать.

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

person matt    schedule 17.03.2017
comment
Спасибо, это лучший ответ на данный момент, фактически говорящий о том, что происходит за кулисами на уровне компилятора. У вас есть ссылка на идею о том, что weak требует больших вычислительных ресурсов? - person ephemer; 21.03.2017
comment
Вы просто должны представить, что нужно ARC, чтобы вести всю эту бухгалтерию за кулисами. Процесс хорошо описан в замечательном видео 406 WWDC 2012, где был представлен ARC. Это видео должен посмотреть каждый, потому что Swift ARC — это ARC для Objective-C (на самом деле мы теперь знаем, что Крис Латтнер изобрел ARC для Objective-C именно для того, чтобы он мог создать Swift). - person matt; 21.03.2017
comment
В любом случае, смысл моего ответа в том, что ваш вопрос основан на ложной предпосылке. Разница между weak и unowned может показаться вам просто заменой одного слова на другое, но это совершенно разные вещи — на самом деле, это разные виды вещей. Возможно, вы захотите прочитать мою онлайн-книгу, в которой я обсуждаю политики управления памятью для синтезированных свойств (по сути, это то же самое, о чем вы спрашиваете) apeth.com/iOSBook/weak равно weak, assign равно unowned. - person matt; 21.03.2017
comment
Они кажутся заменой одного слова другим для тех, кто не задавал вопрос. Итак, предпосылка вопроса, к которому вы скептически относитесь, является вопросом... - person ephemer; 22.03.2017
comment
@ephemer Хорошо, я понимаю, что ты имеешь в виду! Моя проблема заключалась в том, что вопрос начал я понимаю, и я думаю, нет, вы не понимаете. Но теперь я вижу, что вы говорите: я знаю правило, которое гласит, что использовать бесхозный объект можно только в том случае, если объект, на который делается ссылка, всегда существует и необходим для жизни этого объекта. Что вы хотели знать, так это почему это правило (и, во-вторых, есть ли когда-либо случай, когда вы определенно должны использовать неуправляемый, а не слабый, поскольку ясно, что вы всегда может использовать слабый). И вы правы, это совершенно хорошая вещь, чтобы спросить. (Надеюсь, я объяснил это удовлетворительно.) - person matt; 22.03.2017
comment
включает в себя много дополнительных накладных расходов для среды выполнения, поэтому вы говорите, что 1 наносекунда - это много накладных расходов? У вас есть какие-то измерения, чтобы сказать это? По моему опыту, вы можете использовать weak везде, и это не повлияет на вашу производительность, особенно в некоторых приложениях с пользовательским интерфейсом. - person Nikolai Ischuk; 08.06.2018

Ссылка weak на самом деле установлена ​​на nil, и вы должны проверить ее, когда референт освободится, а ссылка unowned будет установлена ​​на nil, но вы не обязаны ее проверять.

Вы можете проверить weak на nil с помощью if let, guard, ? и т. д., но нет смысла проверять unowned, потому что вы думаете, что это невозможно. Если вы ошибаетесь, вы падаете.

Я обнаружил, что на практике я никогда не использую unowned. Это незначительное снижение производительности, но дополнительная безопасность от использования weak стоит того.

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

«Почему он существует», который вы ищете, заключается в том, что Swift предназначен для написания системного кода (например, ядра ОС), и если бы у них не было самых основных примитивов без дополнительного поведения, они не могли бы этого сделать .

ПРИМЕЧАНИЕ. Ранее в этом ответе я уже говорил, что значение unowned не равно нулю. Это неправильно, голый unowned равен нулю. unowned(unsafe) не равно нулю и может быть висячим указателем. Это необходимо для обеспечения высокой производительности и, как правило, не должно быть в коде приложения.

person Lou Franco    schedule 16.03.2017
comment
Кажется, вы пришли к общему мнению с этим (что weak встречается чаще, чем unowned), но я не уверен, что это отвечает на вопрос. Кажется, что ответ описывает поверхностные различия в использовании, а не внутренние рассуждения, почему они существуют как отдельные ключевые слова. Комментарий о том, что Swift также предназначен для системного программирования, хороший, спасибо. - person ephemer; 21.03.2017
comment
Еще один ключевой момент, возвращающийся к вашему ответу, заключается в том, что вы не гарантируете, что ссылки без владельца будут установлены на ноль (я предполагаю, что они обычно не будут и останутся опасными оборванными указателями). Спасибо. - person ephemer; 30.08.2017
comment
Есть unowned(unsafe), которые становятся оборванными указателями. Я обновлю ответ. - person Lou Franco; 30.08.2017