Подтвердить что ты не робот

Зачем создавать "Implicitly Unwrapped Optionals", так как это означает, что вы знаете, что это значение?

Зачем вам создавать "Неявно развернутый необязательный", а не просто обычную переменную или константу? Если вы знаете, что его можно успешно развернуть, тогда зачем создавать дополнительный файл? Например, почему это так:

let someString: String! = "this is the string"

будет более полезным, чем:

let someString: String = "this is the string"

Если "опциональные значения указывают на то, что константе или переменной разрешено не иметь" никакого значения "", но "иногда из структуры программ ясно, что опциональное значение всегда будет иметь значение после того, как это значение будет впервые установлено", в чем смысл сделать его необязательным в первую очередь? Если вы знаете, что необязательное всегда будет иметь значение, разве это не делает его необязательным?

4b9b3361

Ответ 1

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

(Edit) Чтобы быть понятным: регулярные опции всегда предпочтительнее.

Ответ 2

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

По большей части, Косвенно Незавернутые Optionals следует избегать, поскольку при использовании по ошибке, все ваше приложение будет сбой при обращении к нему в то время как nil. Если вы когда-либо не уверены, может ли переменная быть nil, всегда по умолчанию используется обычный Optional. Развертывание переменной, которая никогда не равна nil определенно не повредит.

Ответ 3

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

Например:

// These classes are buddies that never go anywhere without each other
class B {
    var name : String
    weak var myBuddyA : A!
    init(name : String) {
        self.name = name
    }
}

class A {
    var name : String
    var myBuddyB : B
    init(name : String) {
        self.name = name
        myBuddyB = B(name:"\(name) buddy B")
        myBuddyB.myBuddyA = self
    }
}

var a = A(name:"Big A")
println(a.myBuddyB.name)   // prints "Big A buddy B"

Любой экземпляр B должен всегда иметь действительную ссылку myBuddyA, поэтому мы не хотим, чтобы пользователь рассматривал ее как необязательный, но нам нужно, чтобы она была необязательной, чтобы мы могли построить B до мы имеем A для ссылки.

ОДНАКО! Такое взаимное требование ссылки часто является показателем жесткой связи и плохого дизайна. Если вы обнаружите, что полагаетесь на неявно развернутые варианты, вам, вероятно, стоит подумать о рефакторинге, чтобы устранить кросс-зависимости.

Ответ 4

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

Книга Swift, в главе "Основы" , раздел "Неявно отключенные опционы" :

Неявно развернутые опции полезны, когда подтверждается, что значение optionals существует сразу после того, как опционально определено первым и, безусловно, может быть предположительно существовать в каждой точке после этого. Первичное использование неявно развернутых опций в Swift во время инициализации класса, как описано в Unowned References и Implicitly Unwrapped Additional Properties.
...
Вы можете придумать неявно развернутую опцию, которая дает разрешение на автоматическое разворачивание опциона, когда оно используется. Вместо того, чтобы помещать восклицательный знак после имени опций каждый раз, когда вы его используете, вы помещаете восклицательный знак после того, как тип optionals указывается, когда вы его объявляете.

Это сводится к использованию случаев, когда не nil -ность свойств устанавливается с помощью соглашения об использовании и не может быть принудительно применена компилятором во время инициализации класса. Например, свойства UIViewController, которые инициализируются из NIB или Storyboards, где инициализация разделяется на отдельные фазы, но после viewDidLoad() вы можете предположить, что свойства обычно существуют. В противном случае, чтобы удовлетворить компилятор, вы должны были использовать принудительная разворачивание, необязательная привязка или необязательная цепочка только для того, чтобы скрыть основную цель кода.

Над частью книги Swift ссылается также на раздел автоматического подсчета ссылок:

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

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

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

Что касается "Когда использовать неявно развернутые опции в вашем коде?" вопрос. Как разработчик приложения, вы чаще всего сталкиваетесь с ними в методах подписей библиотек, написанных в Objective-C, которые не имеют возможности выражать необязательные типы.

Из С помощью Swift с Cocoa и Objective-C, раздел Работа с нолем:

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

В некоторых случаях вы можете быть абсолютно уверены, что метод или свойство Objective-C никогда не возвращает ссылку на объект nil. Чтобы сделать объекты в этом специальном сценарии более удобными для работы, Swift импортирует типы объектов как неявно развернутые опции. Неявно развернутые необязательные типы включают все функции безопасности дополнительных типов. Кроме того, вы можете напрямую получить доступ к значению без проверки на nil или разворачивания его самостоятельно. Когда вы получаете доступ к значению этого типа необязательного типа без его безопасного развертывания вначале, неявно развернутая опция проверяет, отсутствует ли значение. Если значение отсутствует, возникает ошибка времени выполнения. В результате вы всегда должны проверять и разворачивать неявно разворачиваемую опцию самостоятельно, если вы не уверены, что значение не может отсутствовать.

... и далее здесь лежат dragons

Ответ 5

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

Самый лучший случай, который я видел до сих пор, - это настройка, которая происходит после инициализации объекта, после чего следует, что "гарантировано" следовать этой настройке, например. в контроллере вида:

class MyViewController: UIViewController {

    var screenSize: CGSize?

    override func viewDidLoad {
        super.viewDidLoad()
        screenSize = view.frame.size
    }

    @IBAction printSize(sender: UIButton) {
        println("Screen size: \(screenSize!)")
    }
}

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

В какой-то степени это снижает уровень безопасности. В любом месте, где у вас есть неявно развернутая опция, это место, где ваше приложение может вылететь из строя, если ваша "гарантия" не всегда выполняется, поэтому это функция используется экономно. Кроме того, при использовании ! все время звучит так, как будто вы кричите, и никто не любит это.

Ответ 6

Apple дает отличный пример в языке Swift Programming → Автоматический подсчет ссылок Разрешение сильных ссылочных циклов между экземплярами классов Неопубликованные ссылки и неявно развернутые дополнительные свойства

class Country {
    let name: String
    var capitalCity: City! // Apple finally correct this line until 2.0 Prerelease (let -> var)
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}

Инициализатор для City вызывается из инициализатора для Country. Однако инициализатор для Country не может передать self в инициализатор City до тех пор, пока новый экземпляр Country не будет полностью инициализирован, как описано в Two -фазная инициализация.

Чтобы справиться с этим требованием, вы объявляете свойство capitalCity Country как неявно разворачиваемое необязательное свойство.

Ответ 7

Обоснование неявных опций легче объяснить, сначала взглянув на обоснование принудительной разворачивания.

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

 if let value = optionalWhichTotallyHasAValue {
     println("\(value)")
 } else {
     assert(false)
 }

что не так хорошо, как

println("\(value!)")

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

Ответ 8

Если вы точно знаете, что значение возвращается из необязательного вместо nil, Неявно Unwrapped Optionals использовать для прямого захвата этих значений из опций и необязательных опций не может.

//Optional string with a value
let optionalString: String? = "This is an optional String"

//Declaration of an Implicitly Unwrapped Optional String
let implicitlyUnwrappedOptionalString: String!

//Declaration of a non Optional String
let nonOptionalString: String

//Here you can catch the value of an optional
implicitlyUnwrappedOptionalString = optionalString

//Here you can't catch the value of an optional and this will cause an error
nonOptionalString = optionalString

Так вот в чем разница между использованием

let someString : String! и let someString : String

Ответ 9

Я думаю, что Optional - это плохое имя для этой конструкции, которое сбивает с толку многих начинающих.

Другие языки (например, Kotlin и С#) используют термин Nullable, и это значительно облегчает понимание этого.

Nullable означает, что вы можете присвоить нулевое значение переменной этого типа. Так что, если это Nullable<SomeClassType>, вы можете присвоить ему пустые значения, если это просто SomeClassType, вы не можете. Вот как работает Swift.

Зачем их использовать? Ну, иногда вам нужно иметь нулевые значения, вот почему. Например, когда вы знаете, что хотите иметь поле в классе, но не можете назначить его чему-либо, когда создаете экземпляр этого класса, но вы это сделаете позже. Я не буду приводить примеры, потому что люди уже предоставили их здесь. Я просто пишу это, чтобы отдать свои 2 цента.

Кстати, я предлагаю вам посмотреть, как это работает на других языках, таких как Kotlin и С#.

Вот ссылка, объясняющая эту функцию в Kotlin: https://kotlinlang.org/docs/reference/null-safety.html

Другие языки, такие как Java и Scala, имеют Optional, но они работают иначе, чем Optional в Swift, потому что все типы Java и Scala обнуляются по умолчанию.

В общем, я думаю, что эта функция должна была называться Nullable в Swift, а не как Optional...