Прежде чем я смогу описать варианты использования для неявно развернутых необязательных опций, вы должны уже понять, что за дополнительные и неявно развернутые необязательные компоненты есть в Swift. Если вы этого не сделаете, я рекомендую вам сначала прочитать мою статью о дополнительных
Когда использовать неявно развернутую опцию
Есть две основные причины, по которым можно создать неявно развернутый необязательный параметр. Все это связано с определением переменной, к которой никогда не будет доступа, nil
потому что в противном случае компилятор Swift всегда заставит вас явно развернуть Optional.
1. Константа, которую нельзя определить во время инициализации
Каждая константа-член должна иметь значение к моменту завершения инициализации. Иногда константа не может быть инициализирована с ее правильным значением во время инициализации, но она все еще может иметь значение до доступа.
Использование необязательной переменной позволяет обойти эту проблему, поскольку Optional автоматически инициализируется с помощью, nil
и значение, которое он в конечном итоге будет содержать, по-прежнему будет неизменным. Тем не менее, может быть больно постоянно разворачивать переменную, которая точно не равна нулю. Неявно развернутые опционные компоненты получают те же преимущества, что и опциональные, с дополнительным преимуществом, которое не требует явной разворачивания его повсюду.
Отличным примером этого является ситуация, когда переменная-член не может быть инициализирована в подклассе UIView до тех пор, пока представление не будет загружено:
class MyView: UIView {
@IBOutlet var button: UIButton!
var buttonOriginalWidth: CGFloat!
override func awakeFromNib() {
self.buttonOriginalWidth = self.button.frame.size.width
}
}
Здесь вы не можете рассчитать исходную ширину кнопки, пока не загрузится представление, но вы знаете, что awakeFromNib
он будет вызван перед любым другим методом в представлении (кроме инициализации). Вместо того, чтобы принудительно бессмысленно развертывать значение во всем вашем классе, вы можете объявить его как Неявно Необязательный Необязательный.
2. Когда ваше приложение не может восстановиться от переменной nil
Это должно быть крайне редко, но если ваше приложение не может продолжать работать, если к переменной nil
обращаются, это будет пустой тратой времени на то, чтобы проверить ее nil
. Обычно, если у вас есть условие, которое должно быть абсолютно верным, чтобы ваше приложение продолжало работать, вы должны использовать assert
. Неявно развернутый необязательный параметр имеет встроенное утверждение для nil. Даже тогда часто бывает полезно развернуть необязательное и использовать более описательный атрибут, если он равен нулю.
Когда не использовать неявно развернутую опцию
1. Ленивые вычисляемые переменные-члены
Иногда у вас есть переменная-член, которая никогда не должна быть nil, но она не может быть установлена на правильное значение во время инициализации. Одним из решений является использование Неявного Unwrapped Optional, но лучше использовать переменную lazy:
class FileSystemItem {
}
class Directory : FileSystemItem {
lazy var contents : [FileSystemItem] = {
var loadedContents = [FileSystemItem]()
// load contents and append to loadedContents
return loadedContents
}()
}
Теперь переменная-член contents
не инициализируется до первого обращения к ней. Это дает классу шанс войти в правильное состояние перед вычислением начального значения.
Примечание: это может показаться противоречащим № 1 сверху. Однако есть важное различие, которое необходимо сделать. buttonOriginalWidth
Выше должны быть установлено во viewDidLoad , чтобы предотвратить любой меняющиеся кнопки ширины до свойства доступа.
2. Везде остальное
По большей части следует избегать неявно развернутых дополнительных компонентов, поскольку при неправильном использовании все приложение будет аварийно завершено при обращении к нему во время nil
. Если вы когда-либо не уверены, может ли переменная быть nil, всегда по умолчанию используется обычный Optional. Развертывание переменной, которая никогда не будет, nil
конечно, не повредит.
if someOptional
.hasValue
определяется прямо на Необязательный. Я предпочитаю семантикуhasValue
тем из!= nil
. Я чувствую, что это гораздо более понятно для начинающих программистов, которые не использовалиnil
другие языки.hasValue
гораздо логичнее, чемnil
.hasValue
его вытащили из бета-версии 6. Эш отложил его обратно ... github.com/AshFurrow/hasValueНеявно развернутые необязательные параметры полезны для представления свойства как необязательного, когда на самом деле оно должно быть необязательным под прикрытием. Это часто необходимо для «связывания узла» между двумя связанными объектами, каждый из которых нуждается в ссылке на другой. Это имеет смысл, когда ни одна ссылка на самом деле не является обязательной, но одна из них должна быть нулевой, пока пара инициализируется.
Например:
Любой
B
экземпляр должен всегда иметь допустимуюmyBuddyA
ссылку, поэтому мы не хотим, чтобы пользователь рассматривал его как необязательный, но нам нужно, чтобы он был необязательным, чтобы мы могли создатьB
до того, как у насA
будет ссылка.ОДНАКО! Такое требование взаимной ссылки часто свидетельствует о сильной связи и плохой конструкции. Если вы полагаетесь на неявно развернутые опции, вам, вероятно, следует рассмотреть возможность рефакторинга для устранения перекрестных зависимостей.
источник
@IBOutlet
weak
декларацию и удалив "без создания сильного цикла сохранения"Неявно развернутые опционы являются прагматическим компромиссом, чтобы сделать работу в гибридной среде, которая должна взаимодействовать с существующими средами Какао и их соглашениями, более приятной, и в то же время допускать пошаговую миграцию в более безопасную парадигму программирования - без нулевых указателей - принудительно примененную компилятором Swift.
В книге Swift, в главе «Основы» , раздел « Неявно развернутые дополнительные компоненты» говорится:
Это сводится к тому, использовать случаи , когда не-
nil
-ness свойств устанавливаются посредством использования конвенции, и не может быть насильственным компилятором во время инициализации класса. Например,UIViewController
свойства, которые инициализируются из NIB или раскадровок, где инициализация разделена на отдельные фазы, но послеviewDidLoad()
вы можете предположить, что свойства обычно существуют. В противном случае, чтобы удовлетворить требования компилятора, вы должны были использовать принудительное развертывание , необязательное связывание или необязательное сцепление только для того, чтобы скрыть основное назначение кода.Выше часть книги Swift относится также к главе « Автоматический подсчет ссылок» :
Это сводится к тому, что язык не является сборщиком мусора, поэтому прерывание циклов сохранения лежит на вас, как на программисте, и неявно развернутые опции являются инструментом, позволяющим скрыть эту причуду.
Это охватывает «Когда использовать неявно развернутые опции в вашем коде?» вопрос. Как разработчик приложения, вы чаще всего встречаетесь с ними в сигнатурах методов библиотек, написанных на Objective-C, которые не имеют возможности выражать необязательные типы.
Из использования Swift с Какао и Objective-C, раздел Работа с nil :
... и за ее пределами
источник
Простые примеры, состоящие из одной строки (или нескольких строк), не очень хорошо описывают поведение дополнительных функций - да, если вы объявляете переменную и сразу предоставляете ей значение, в дополнительном элементе нет смысла.
Наилучший случай, который я видел до сих пор, - это настройка, которая происходит после инициализации объекта, за которой следует использование, которое «гарантированно» следует этой настройке, например, в контроллере представления:
Мы знаем, что
printSize
он будет вызван после загрузки представления - это метод действия, подключенный к элементу управления внутри этого представления, и мы постарались не вызывать его иначе. Таким образом, мы можем сэкономить некоторые дополнительные проверки / привязки с помощью!
. Swift не может распознать эту гарантию (по крайней мере, пока Apple не решит проблему остановки), поэтому вы сообщаете компилятору, что она существует.Это в какой-то степени нарушает безопасность типов. В любом месте, где у вас есть неявно развернутый необязательный параметр, это место, в котором может произойти сбой вашего приложения, если ваша «гарантия» не всегда сохраняется, поэтому эту функцию следует использовать экономно. Кроме того, постоянное использование
!
звучит так, будто ты кричишь, и это никому не нравится.источник
CGSizeZero
может быть хорошим ценностным показателем в реальном использовании. Но что, если у вас есть размер, загруженный из пера, который может фактически быть нулем? Тогда использованиеCGSizeZero
в качестве часового не поможет вам отличить неустановленное значение от нуля. Более того, это в равной степени относится и к другим типам, загружаемым из nib (или где-либо еще послеinit
): строкам, ссылкам наlet foo? = 42
, скорее, нетlet foo! = 42
). Это не относится к этому. (Имейте в виду, что это может быть релевантный ответ об опциях, но не о неявно развернутых опциях, которые являются другим / связанным животным.)Apple приводит прекрасный пример в языке программирования Swift -> Автоматический подсчет ссылок -> Устранение циклов сильных ссылок между экземплярами классов -> Неизвестные ссылки и Неявно развернутые дополнительные свойства
источник
Обоснование неявных опционов легче объяснить, сначала посмотрев на обоснование принудительного развертывания.
Принудительное развертывание необязательного (неявного или нет), используя! оператор означает, что вы уверены, что в вашем коде нет ошибок, а в дополнительном уже есть значение, в которое он разворачивается. Без ! оператор, вы, вероятно, просто заявите с необязательной привязкой:
что не так хорошо, как
Теперь неявные необязательные параметры позволяют вам выразить наличие необязательного значения, которое, как вы ожидаете, всегда будет иметь значение в развернутом виде во всех возможных потоках. Так что это просто шаг вперед, помогая вам - ослабив требование написания! развертывать каждый раз и гарантировать, что среда выполнения все равно будет содержать ошибку в случае, если ваши предположения о потоке неверны.
источник
init
(что вы даже не могли бы реализовать), но они ' повторно гарантированно будет установлен после того, какawakeFromNib
/viewDidLoad
.Если вы точно знаете, что значение, возвращаемое из необязательного значения вместо
nil
, неявно развернутые опциональные значения используются для прямого захвата этих значений из опциональных, а не необязательные - не могут.Так что это разница между использованием
let someString : String!
а такжеlet someString : String
источник
Я думаю
Optional
, это плохое имя для этой конструкции, которая смущает многих начинающих.Другие языки (например, Kotlin и C #) используют этот термин
Nullable
, и это значительно облегчает его понимание.Nullable
означает, что вы можете присвоить нулевое значение переменной этого типа. Так что, если это такNullable<SomeClassType>
, вы можете присвоить ему пустые значения, если это простоSomeClassType
, вы не можете. Вот как работает Swift.Зачем их использовать? Ну, иногда вам нужно иметь нулевые значения, вот почему. Например, когда вы знаете, что хотите иметь поле в классе, но не можете назначить его чему-либо, когда создаете экземпляр этого класса, но вы это сделаете позже. Я не буду приводить примеры, потому что люди уже предоставили их здесь. Я просто пишу это, чтобы отдать свои 2 цента.
Кстати, я предлагаю вам посмотреть, как это работает на других языках, таких как Kotlin и C #.
Вот ссылка, объясняющая эту функцию в Kotlin: https://kotlinlang.org/docs/reference/null-safety.html
В других языках, таких как Java и Scala, есть
Optional
s, но они работают иначе, чемOptional
s в Swift, потому что типы Java и Scala все обнуляются по умолчанию.В общем, я думаю, что эта функция должна была быть названа
Nullable
в Swift, а неOptional
...источник
Implicitly Unwrapped Optional
это синтаксический сахарOptional
, который не заставляет программиста развернуть переменную. Он может использоваться для переменной, которая не может быть инициализирована во времяtwo-phase initialization process
и подразумевает не ноль. Эта переменная ведет себя как не ноль, но на самом деле является необязательной переменной. Хороший пример - интерфейс ИнтерфейсOptional
обычно предпочтительнееисточник