Необязательный параметр в Swift - это тип, который может содержать значение или не иметь значения. Необязательные параметры записываются путем добавления ?
к любому типу:
var name: String? = "Bertie"
Опции (наряду с универсальными шаблонами) - одна из самых сложных для понимания концепций Swift. Из-за того, как они написаны и используются, легко получить неправильное представление о том, что они из себя представляют. Сравните приведенное выше необязательное действие с созданием обычной строки:
var name: String = "Bertie" // No "?" after String
Судя по синтаксису, необязательная строка очень похожа на обычную строку. Это не. Необязательная строка не является строкой с включенной необязательной настройкой. Это не особая разновидность String. Строка и необязательная строка - это совершенно разные типы.
Вот самое важное, что нужно знать: необязательный параметр - это своего рода контейнер. Необязательная строка - это контейнер, который может содержать строку. Необязательный Int - это контейнер, который может содержать Int. Думайте о необязательном пакете как о посылке. Прежде чем открыть его (или развернуть на языке опций), вы не узнаете, содержит ли он что-то или ничего.
Вы можете увидеть как реализованы опции в Стандартная библиотека Swift. Для этого введите «Необязательно» в любой файл Swift и щелкните по нему ⌘-щелчком. Вот важная часть определения:
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
Необязательный - это просто enum
, который может быть одним из двух случаев: .none
или .some
. Если это .some
, есть связанное значение, которое в приведенном выше примере будет String
Hello. Необязательный параметр использует Generics для присвоения типа связанному значению. Тип необязательной строки - это не String
, это Optional
или, точнее, Optional<String>
.
Все, что Swift делает с опциями, - это волшебство, делающее чтение и написание кода более плавным. К сожалению, это не позволяет понять, как это работает на самом деле. Я рассмотрю некоторые уловки позже.
Примечание. Я буду много говорить о необязательных переменных, но также можно создавать необязательные константы. Я помечаю все переменные их типами, чтобы упростить понимание создаваемых типов типов, но это не обязательно в вашем собственном коде.
Как создать опционалы
Чтобы создать необязательный, добавьте ?
после типа, который вы хотите обернуть. Любой тип может быть необязательным, даже ваши собственные типы. Между типом и ?
не может быть пробела.
var name: String? = "Bob" // Create an optional String that contains "Bob"
var peter: Person? = Person() // An optional "Person" (custom type)
// A class with a String and an optional String property
class Car {
var modelName: String // must exist
var internalName: String? // may or may not exist
}
Использование опционалов
Вы можете сравнить необязательный параметр с nil
, чтобы узнать, есть ли у него значение:
var name: String? = "Bob"
name = nil // Set name to nil, the absence of a value
if name != nil {
print("There is a name")
}
if name == nil { // Could also use an "else"
print("Name has no value")
}
Это немного сбивает с толку. Подразумевается, что необязательным является либо то, либо другое. Либо ноль, либо это Боб. Это неправда, необязательное не трансформируется во что-то другое. Сравнение его с nil - это уловка, позволяющая упростить чтение кода. Если необязательный параметр равен nil, это просто означает, что для перечисления в настоящее время установлено значение .none
.
Только опции могут быть нулевыми
Если вы попытаетесь установить для необязательной переменной значение nil, вы получите сообщение об ошибке.
var red: String = "Red"
red = nil // error: nil cannot be assigned to type 'String'
Другой способ рассматривать опциональные параметры - это дополнение к обычным переменным Swift. Они являются аналогом переменной, которая гарантированно имеет значение. Swift - осторожный язык, который ненавидит двусмысленность. Большинство переменных определены как необязательные, но иногда это невозможно. Например, представьте себе контроллер представления, который загружает изображение либо из кеша, либо из сети. Он может иметь или не иметь это изображение во время создания контроллера представления. Невозможно гарантировать значение переменной изображения. В этом случае вам придется сделать это необязательным. Он начинается с nil
, и когда изображение извлекается, необязательный элемент получает значение.
Использование необязательного параметра раскрывает намерения программиста. По сравнению с Objective-C, где любой объект может быть нулевым, Swift требует, чтобы вы четко понимали, когда значение может отсутствовать, а когда оно гарантированно существует.
Чтобы использовать необязательный параметр, разверните его
Необязательный String
не может использоваться вместо фактического String
. Чтобы использовать обернутое значение внутри необязательного параметра, вы должны его развернуть. Самый простой способ развернуть необязательный элемент - добавить !
после имени необязательного элемента. Это называется принудительным разворачиванием. Он возвращает значение внутри необязательного (как исходный тип), но если необязательный параметр nil
, это вызывает сбой во время выполнения. Перед разворачиванием убедитесь, что есть значение.
var name: String? = "Bob"
let unwrappedName: String = name!
print("Unwrapped name: \(unwrappedName)")
name = nil
let nilName: String = name! // Runtime crash. Unexpected nil.
Проверка и использование необязательного
Поскольку вы всегда должны проверять наличие nil перед разворачиванием и использованием необязательного параметра, это общий шаблон:
var mealPreference: String? = "Vegetarian"
if mealPreference != nil {
let unwrappedMealPreference: String = mealPreference!
print("Meal: \(unwrappedMealPreference)") // or do something useful
}
В этом шаблоне вы проверяете наличие значения, а затем, когда вы уверены, что оно есть, вы принудительно разворачиваете его во временную константу для использования. Поскольку это обычное дело, Swift предлагает ярлык с использованием if let. Это называется необязательной привязкой.
var mealPreference: String? = "Vegetarian"
if let unwrappedMealPreference: String = mealPreference {
print("Meal: \(unwrappedMealPreference)")
}
Это создает временную константу (или переменную, если вы замените let
на var
), область действия которой находится только в фигурных скобках if. Поскольку необходимость использования таких имен, как unwrappedMealPreference или realMealPreference, является обузой, Swift позволяет повторно использовать исходное имя переменной, создавая временное имя в пределах области скобок.
var mealPreference: String? = "Vegetarian"
if let mealPreference: String = mealPreference {
print("Meal: \(mealPreference)") // separate from the other mealPreference
}
Вот код, демонстрирующий, что используется другая переменная:
var mealPreference: String? = "Vegetarian"
if var mealPreference: String = mealPreference {
print("Meal: \(mealPreference)") // mealPreference is a String, not a String?
mealPreference = "Beef" // No effect on original
}
// This is the original mealPreference
print("Meal: \(mealPreference)") // Prints "Meal: Optional("Vegetarian")"
Необязательная привязка работает, проверяя, равно ли необязательное значение nil. В противном случае он разворачивает необязательный элемент в предоставленную константу и выполняет блок. В Xcode 8.3 и новее (Swift 3.1) попытка напечатать необязательный параметр, подобный этому, вызовет бесполезное предупреждение. Используйте необязательный debugDescription
, чтобы заглушить его:
print("\(mealPreference.debugDescription)")
Для чего нужны опции?
Варианты имеют два варианта использования:
- Вещи, которые могут потерпеть неудачу (я чего-то ожидал, но ничего не получил)
- Вещи, которые сейчас ничто, но могут быть чем-то позже (и наоборот)
Несколько конкретных примеров:
- Свойство, которое может быть там или не быть, например
middleName
или spouse
в Person
классе.
- Метод, который может возвращать значение или ничего, например поиск совпадения в массиве.
- Метод, который может вернуть либо результат, либо ошибку и ничего не вернуть, например, попытка прочитать содержимое файла (которое обычно возвращает данные файла), но файл не существует.
- Свойства делегата, которые не всегда нужно устанавливать и обычно устанавливаются после инициализации
- Для
weak
свойств в классах. То, на что они указывают, можно в любой момент установить на nil
.
- Большой ресурс, который, возможно, придется освободить, чтобы освободить память.
- Когда вам нужен способ узнать, когда значение было установлено (данные еще не загружены ›данные), вместо использования отдельного файла dataLoaded
Boolean
В Objective-C нет дополнительных опций, но есть эквивалентная концепция, возвращающая nil. Вместо этого методы, которые могут возвращать объект, могут возвращать nil. Это означает отсутствие действительного объекта и часто используется, чтобы сказать, что что-то пошло не так. Он работает только с объектами Objective-C, но не с примитивами или базовыми C-типами (перечисления, структуры). Objective-C часто имел специализированные типы для представления отсутствия этих значений (NSNotFound
, что на самом деле NSIntegerMax
, kCLLocationCoordinate2DInvalid
для представления недопустимой координаты, -1
или некоторые отрицательные значения также используются). Кодировщик должен знать об этих особых значениях, поэтому они должны быть задокументированы и изучены для каждого случая. Если метод не может принимать nil
в качестве параметра, это необходимо задокументировать. В Objective-C nil
был указателем, так же как все объекты были определены как указатели, но nil
указывал на конкретный (нулевой) адрес. В Swift nil
- это литерал, который означает отсутствие определенного типа.
По сравнению с nil
Раньше вы могли использовать любой необязательный параметр в качестве Boolean
:
let leatherTrim: CarExtras? = nil
if leatherTrim {
price = price + 1000
}
В более поздних версиях Swift вы должны использовать leatherTrim != nil
. Почему это? Проблема в том, что Boolean
можно заключить в необязательный. Если у вас Boolean
вот так:
var ambiguous: Boolean? = false
у него есть два вида ложных значений: в одном случае нет значения, а в другом есть значение, но значение равно false
. Swift ненавидит двусмысленность, поэтому теперь вы всегда должны проверять необязательный параметр на nil
.
Вы можете спросить, в чем смысл необязательного Boolean
? Как и в случае с другими опциями, состояние .none
может указывать на то, что значение пока неизвестно. На другом конце сетевого вызова может быть что-то, для опроса которого требуется некоторое время. Необязательные логические значения также называются трехзначными логическими значениями.
Быстрые трюки
Swift использует некоторые приемы, позволяющие работать с опциями. Рассмотрим эти три строки обычного необязательного кода;
var religiousAffiliation: String? = "Rastafarian"
religiousAffiliation = nil
if religiousAffiliation != nil { ... }
Ни одна из этих строк не должна компилироваться.
- Первая строка устанавливает необязательную строку с использованием литерала String, двух разных типов. Даже если бы это был
String
типы разные
- Вторая строка устанавливает для необязательной String значение nil, два разных типа
- Третья строка сравнивает необязательную строку с nil, два разных типа
Я рассмотрю некоторые детали реализации опций, которые позволяют этим строкам работать.
Создание необязательного
Использование ?
для создания необязательного - это синтаксический сахар, разрешенный компилятором Swift. Если вы хотите сделать это долгим путем, вы можете создать необязательный параметр, например:
var name: Optional<String> = Optional("Bob")
Это вызывает первый инициализатор Optional
, public init(_ some: Wrapped)
, который определяет связанный тип необязательного элемента из типа, используемого в круглых скобках.
Еще более длительный способ создания и установки необязательного:
var serialNumber:String? = Optional.none
serialNumber = Optional.some("1234")
print("\(serialNumber.debugDescription)")
Установка необязательного значения nil
Вы можете создать необязательный параметр без начального значения или создать его с начальным значением nil
(оба имеют одинаковый результат).
var name: String?
var name: String? = nil
Разрешение дополнительных параметров равным nil
разрешено протоколом ExpressibleByNilLiteral
(ранее называвшимся NilLiteralConvertible
). Необязательный параметр создается вторым инициализатором Optional
, public init(nilLiteral: ())
. В документации говорится, что вы не должны использовать ExpressibleByNilLiteral
ни для чего, кроме дополнительных опций, поскольку это изменит значение nil в вашем коде, но это возможно:
class Clint: ExpressibleByNilLiteral {
var name: String?
required init(nilLiteral: ()) {
name = "The Man with No Name"
}
}
let clint: Clint = nil // Would normally give an error
print("\(clint.name)")
Тот же протокол позволяет вам установить для уже созданного необязательного значения nil
. Хотя это не рекомендуется, вы можете использовать инициализатор литерала nil напрямую:
var name: Optional<String> = Optional(nilLiteral: ())
Сравнение необязательного с nil
Необязательные параметры определяют два специальных оператора == и! =, Которые вы можете увидеть в определении Optional
. Первый ==
позволяет вам проверить, равен ли какой-либо необязательный параметр нулю. Два разных параметра, для которых установлено значение .none, всегда будут равны, если соответствующие типы одинаковы. Когда вы сравниваете с nil, за кулисами Swift создает необязательный параметр того же связанного типа, для которого установлено значение .none, а затем используется его для сравнения.
// How Swift actually compares to nil
var tuxedoRequired: String? = nil
let temp: Optional<String> = Optional.none
if tuxedoRequired == temp { // equivalent to if tuxedoRequired == nil
print("tuxedoRequired is nil")
}
Второй оператор ==
позволяет сравнивать два варианта. Оба должны быть одного типа, и этот тип должен соответствовать Equatable
(протокол, который позволяет сравнивать вещи с помощью обычного оператора ==). Swift (предположительно) разворачивает два значения и сравнивает их напрямую. Он также обрабатывает случай, когда одна или обе опции равны .none
. Обратите внимание на различие между сравнением с литералом nil
.
Кроме того, он позволяет сравнивать любой тип Equatable
с дополнительной оболочкой этого типа:
let numberToFind: Int = 23
let numberFromString: Int? = Int("23") // Optional(23)
if numberToFind == numberFromString {
print("It's a match!") // Prints "It's a match!"
}
За кулисами Swift оборачивает необязательное как необязательное перед сравнением. Он также работает с литералами (if 23 == numberFromString {
)
Я сказал, что есть два оператора ==
, но на самом деле есть третий, который позволяет вам поместить nil
в левую часть сравнения.
if nil == name { ... }
Варианты именования
Не существует соглашения Swift для наименования необязательных типов, отличных от необязательных типов. Люди избегают добавлять что-то к имени, чтобы показать, что это необязательный тип (например, optionalMiddleName или possibleNumberAsString), и позволяют объявлению показать, что это необязательный тип. Это становится трудным, когда вы хотите назвать что-то, что содержало бы значение из необязательного. Имя middleName подразумевает, что это тип String, поэтому, когда вы извлекаете из него значение String, вы часто можете получить такие имена, как actualMiddleName или unwrappedMiddleName или realMiddleName. Используйте необязательную привязку и повторно используйте имя переменной, чтобы обойти это.
Официальное определение
Из Основы языка программирования Swift:
Swift также вводит необязательные типы, которые обрабатывают отсутствие значения. Необязательные параметры говорят либо «есть значение, и оно равно x», либо «значения нет вообще». Необязательные параметры аналогичны использованию nil с указателями в Objective-C, но они работают для любого типа, а не только для классов. Опции более безопасны и выразительны, чем нулевые указатели в Objective-C, и лежат в основе многих самых мощных функций Swift.
Необязательные параметры - это пример того, что Swift - это типобезопасный язык. Swift помогает вам понять, с какими типами значений может работать ваш код. Если часть вашего кода ожидает String, безопасность типов не позволяет вам по ошибке передать ему Int. Это позволяет выявлять и исправлять ошибки как можно раньше в процессе разработки.
В заключение, вот стихотворение 1899 года о вариантах:
Вчера на лестнице
я встретила человека, которого там не было
Сегодня его не было здесь снова
Я бы хотел, чтобы он ушел < br /> Антигонизм
Дополнительные ресурсы:
person
nevan king
schedule
03.06.2014