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

В чем разница между String? и String! (два способа создания необязательной переменной)?

В Язык быстрого языка (книга Apple). Я читал, что вы можете создавать необязательные переменные двумя способами: используя знак вопроса (?) или используя восклицательный знак (!).

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

var str: String? = "Question mark?"
println(str!) // Exclamation mark needed
str = nil    

Пока с помощью (!) вы можете получить его без суффикса:

var str: String! = "Exclamation mark!"
println(str) // No suffix needed
str = nil

В чем разница и почему существуют два способа, если вообще нет разницы?

4b9b3361

Ответ 1

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

Класс A ↔ Класс B

Процедура класса A init должна создавать (и собственный) класс B, а B нуждается в слабом обращении к A:

class A {
    let instanceOfB: B!
    init() {
        self.instanceOfB = B(instanceOfA: self)
    }
}

class B {
    unowned let instanceOfA: A
    init(instanceOfA: A) {
        self.instanceOfA = instanceOfA
    }
}

Теперь,

  • Класс B требует ссылки на класс A, который должен быть инициализирован.
  • Класс A может передать только self в инициализатор класса B после полной инициализации.
  • Для того чтобы класс A считался инициализированным до создания класса B, свойство instanceOfB должно быть необязательным.

Однако, как только A был создан, было бы очень неприятно иметь доступ к instanceOfB, используя instanceOfB! так как мы знаем, что должен быть B

Чтобы этого избежать, instanceOfB объявляется как необязательный (необязательный) экземпляр (instanceOfB!), и мы можем получить к нему доступ, используя только instanceOfB. (Кроме того, я подозреваю, что компилятор также может оптимизировать доступ по-разному).

Пример этого приведен на страницах с 464 по 466 книги.

Резюме:

  • Использовать? если значение может стать нулевым в будущем, так что вы проверите это.
  • Используйте! если он действительно не станет нулевым в будущем, но сначала он должен быть ноль.

Ответ 2

Вы должны выйти за пределы синтаксического сахара.

Существует два совершенно разных полиморфных типа. Синтаксический сахар просто использует один или другой из этих типов.

Когда вы пишете Foo? как тип, у вас действительно есть Optional<Foo>, а когда вы пишете Foo!, у вас действительно есть ImplicitlyUnwrappedOptional<Foo>.

Это два разных типа, и они отличаются от Foo.

Ответ 3

Тип String! называется неявно развернутым необязательным:

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

Эти типы опций определяются как неявно развернутые ДОПОЛНИТЕЛЬНО. Вы пишете неявно разворачиваемую опцию, помещая восклицательный знак (String!), а не знак вопроса (String?) после тип, который вы хотите сделать необязательным.

Ответ 4

Значения, создаваемые с помощью ?, являются обычными необязательными значениями, как вы упомянули, вы должны получить к нему доступ через необязательную привязку (if let unwrappedValue = myOptionalValue) или с помощью синтаксиса восклицательных знаков myOptionalValue!.doSomething().

Значения, созданные с помощью !, называются неявно развернутыми опциями. С ними вам не нужно вручную распаковывать, прежде чем использовать их. Когда вы выполните val myOptionalValue!.doSomething().

Значение будет автоматически развернуто для вас, когда вы используете myOptionalValue напрямую, будьте осторожны с этим, потому что доступ к неявно разворачиваемому значению, когда в нем нет никакого значения (когда он равен nil), приведет к ошибка времени выполнения.

Ответ 5

в необязательной цепочке вы найдете ответ:

пример класса:

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

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

let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error

Вышеприведенный код успешно выполняется, когда значение john.residence имеет значение, отличное от нуля, и задает значение roomCount для значения Int, содержащего соответствующее количество комнат. Однако этот код всегда вызывает ошибку времени выполнения, когда место жительства равно нулю, как показано выше.

Дополнительная цепочка предоставляет альтернативный способ доступа к значению numberOfRooms. Чтобы использовать дополнительную цепочку, используйте знак вопроса вместо восклицательного знака:

if let roomCount = john.residence?.numberOfRooms {
    println("John residence has \(roomCount) room(s).")
} else {
    println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."

Ответ 6

Ну, упомянутый выше @tarmes. Заметил другое использование неявного необязательного:

Предположим, у меня есть необязательный Int:

let firstInt: Int? = 9

И я пытаюсь использовать опциональное сопоставление шаблонов и использовать этот необязательный Int следующим образом:

if case let myFirstInt? = firstInt where myFirstInt > 1 {
    print("Valid")
} else {
    print("Invalid")
}

Обратите внимание, что я использую неявный необязательный параметр с локальным параметром myFirstInt, что делает его безопасным для условия nil, связанного с необязательным firstInt. Если теперь, я делаю firstInt как nil, он выполнит условие else. Если вместо этого я использую force-unwrap с firstInt, который приведет к сбою, что-то вроде этого:

введите описание изображения здесь