Что означает «фатальная ошибка: неожиданно найденный ноль при развертывании необязательного значения»?

331

Моя программа Swift сбой с EXC_BAD_INSTRUCTION и эта ошибка. Что это значит и как я могу это исправить?

фатальная ошибка: неожиданно найден nil при развертывании необязательного значения


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

  • 6
    @RobertColumbia, это пара вопросов и ответов сообщества Wiki с обширной документацией по проблеме, я не думаю, что этот вопрос должен быть закрыт из-за отсутствия MCVE.
  • 0
    Создайте переменную, как это [var nameOfDaughter: String? ] и используйте эту переменную следующим образом [if let _ = nameOfDaughter {}] - лучший подход.
Теги:
exception
optional

11 ответов

526
Лучший ответ

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

Предыстория: Что является дополнительным?

В Swift Optional - это общий тип, который может содержать значение (любого типа) или вообще не имеет значения.

Во многих других языках программирования особое значение "дозорного" часто используется для указания отсутствия значения. В Objective-C, например, nil (нулевой указатель) указывает на отсутствие объекта. Но это становится более сложным при работе с примитивными типами - следует ли использовать -1 для указания отсутствия целого числа, или, возможно, INT_MIN или какого-либо другого целого числа? Если какое-либо конкретное значение выбрано так, чтобы оно означало "no integer", это означает, что он больше не может считаться допустимым значением.

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

В Swift любой тип можно сделать необязательным. Необязательное значение может принимать любое значение из исходного типа или специальное значение nil.

Опционы определяются с помощью ? суффикс по типу:

var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int?    // 'nil' is the default when no value is provided

Отсутствие значения в необязательном значении обозначается nil:

anOptionalInt = nil

(Обратите внимание, что этот nil не совпадает с nil в Objective-C. В Objective-C nil - это отсутствие действительного указателя объекта, а в Swift - необязательные объекты не ограничены объектами/ссылочными типами. Необязательно ведет себя аналогично Haskell Может быть.)


Почему я получил "фатальную ошибку: неожиданно нашел нуль при развертывании необязательного значения"?

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

Xcode покажет вам сбой, выделив строку кода. Проблема возникает в этой строке.

Изображение 1664

Эта авария может произойти с двумя различными видами разворота:

1. Явная принудительная развязка

Это делается с помощью ! оператор по дополнительному заказу. Например:

let anOptionalString: String?
print(anOptionalString!) // <- CRASH

Поскольку anOptionalString здесь nil, вы получите крах на линии, где вы принудительно разворачиваете его.

2. Неявно отключенные опции

Они определены с помощью ! , а не ? после типа.

var optionalDouble: Double!   // this value is implicitly unwrapped wherever it used

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

print(optionalDouble) // <- CRASH

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

Изображение 1665

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


Когда я должен принудительно развернуть Необязательный?

Явная принудительная развязка

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

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

Неявно отключенные опции

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

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

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


Как я могу безопасно работать с опциями?

Самый простой способ проверить, содержит ли опциональное значение значение, - сравнить его с nil.

if anOptionalInt != nil {
    print("Contains a value!")
} else {
    print("Doesnt contain a value.")
}

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

Дополнительное связывание

Необязательное связывание позволяет проверить, содержит ли опционально значение - и позволяет назначить развернутое значение новой переменной или константе. Он использует синтаксис, if let x = anOptional {...} или if var x = anOptional {...}, в зависимости от необходимости изменения значения новой переменной после привязки.

Например:

if let number = anOptionalInt {
    print("Contains a value! It is \(number)!")
} else {
    print("Doesnt contain a number")
}

Что это значит, сначала проверьте, что опция содержит значение. Если это так, то "развернутое значение присваивается новой переменной (number)), которую вы можете свободно использовать, как если бы она была необязательной. Если опция не содержит значения, то будет предложено предложение else, как и следовало ожидать.

Что является опрятным о необязательном связывании, вы можете одновременно развернуть несколько опций. Вы можете просто отделить инструкции запятой. Заявление будет успешным, если все опции будут развернуты.

var anOptionalInt : Int?
var anOptionalString : String?

if let number = anOptionalInt, let text = anOptionalString {
    print("anOptionalInt contains a value: \(number). And so does anOptionalString, its: \(text)")
} else {
    print("One or more of the optionals dont contain a value")
}

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

if let number = anOptionalInt, number > 0 {
    print("anOptionalInt contains a value: \(number), and its greater than zero!")
}

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

Оператор охраны позволяет определить условие для успеха - и текущая область действия будет продолжать выполнение только в том случае, если это условие выполнено. Они определены с guard condition else {...} синтаксического guard condition else {...}.

Поэтому, чтобы использовать их с необязательным связыванием, вы можете сделать это:

guard let number = anOptionalInt else {
    return
}

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

Если anOptionalInt содержит значение, оно будет развернуто и присвоено новой константе number. Затем код после охраны продолжит выполнение. Если он не содержит значения - защитник выполнит код в скобках, что приведет к передаче элемента управления, так что код сразу после этого не будет выполнен.

Реальная аккуратная вещь о сторожевых заявлениях - это разворачиваемое значение, которое теперь доступно для использования в коде, который следует за оператором (как мы знаем, будущий код может выполняться только в случае, если опция имеет значение). Это отлично подходит для устранения "пирамид смерти", создаваемых вложением нескольких операторов if.

Например:

guard let number = anOptionalInt else {
    return
}

print("anOptionalInt contains a value, and its: \(number)!")

Охранники также поддерживают те же самые аккуратные трюки, которые поддерживаются оператором if, например, разворачивание нескольких опций в одно и то же время и использование предложения where.

Независимо от того, используете ли вы оператор if или guard, все зависит от того, требует ли какой-либо будущий код необязательный параметр, чтобы содержать значение.

Nil Coalescing Operator

Nil Coalescing Operator - отличная сокращенная версия тернарного условного оператора, в первую очередь предназначенная для преобразования опций в не-опционные. Он имеет синтаксис a?? b a?? b, где a - необязательный тип, а b - тот же тип, что a (хотя обычно не факультативный).

Это, по сути, позволяет вам сказать: "Если a содержит значение, разворачивайте его. Если он не возвращает, то вместо b ". Например, вы можете использовать его следующим образом:

let number = anOptionalInt ?? 0

Это определит константу number типа Int, которая либо будет содержать значение anOptionalInt, если оно содержит значение, либо 0 противном случае.

Его справедливая стенография для:

let number = anOptionalInt != nil ? anOptionalInt! : 0

Необязательная цепочка

Вы можете использовать опциональную цепочку для вызова метода или доступа к объекту на необязательном. Это просто делается путем суффикса имени переменной с помощью ? при использовании.

Например, скажем, у нас есть переменная foo, типа дополнительного экземпляра Foo.

var foo : Foo?

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

foo?.doSomethingInteresting()

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

(Это похоже на поведение отправки сообщений в nil в Objective-C)

Это может также использоваться для установки свойств, а также методов вызова. Например:

foo?.bar = Bar()

Опять же, ничего плохого не случится здесь, если foo является nil. Ваш код просто продолжит выполнение.

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

(Это связано с тем, что необязательное значение вернет Void? а не Void на метод, который ничего не возвращает)

Например:

if (foo?.bar = Bar()) != nil {
    print("bar was set successfully")
} else {
    print("bar wasnt set successfully")
}

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

Кроме того, как следует из названия, вы можете "объединить эти утверждения вместе". Это означает, что если foo имеет необязательное свойство baz, которое имеет свойство qux вы можете написать следующее:

let optionalQux = foo?.baz?.qux

Опять же, поскольку foo и baz являются необязательными, значение, возвращаемое из qux, всегда будет необязательным, независимо от того, является ли qux сам по себе необязательным.

map и flatMap map

Часто недоиспользуемая функция с flatMap - это возможность использовать функции map и flatMap. Они позволяют применять необязательные преобразования к необязательным переменным. Если параметр имеет значение, вы можете применить к нему заданное преобразование. Если он не имеет значения, он останется nil.

Например, скажем, у вас есть необязательная строка:

let anOptionalString:String?

Применяя к нему функцию map - мы можем использовать функцию stringByAppendingString чтобы stringByAppendingString ее с другой строкой.

Поскольку stringByAppendingString принимает необязательный строковый аргумент, мы не можем напрямую вводить нашу необязательную строку. Однако, используя map, мы можем использовать allow stringByAppendingString для использования, если anOptionalString имеет значение.

Например:

var anOptionalString:String? = "bar"

anOptionalString = anOptionalString.map {unwrappedString in
    return "foo".stringByAppendingString(unwrappedString)
}

print(anOptionalString) // Optional("foobar")

Однако, если anOptionalString не имеет значения, map вернет nil. Например:

var anOptionalString:String?

anOptionalString = anOptionalString.map {unwrappedString in
    return "foo".stringByAppendingString(unwrappedString)
}

print(anOptionalString) // nil

flatMap работает аналогично map, за исключением того, что позволяет вам возвращать еще один факультативный flatMap из тела закрытия. Это означает, что вы можете вводить опцию в процесс, который требует нестандартного ввода, но может выводить необязательный сам.

try!

Быстрая система обработки ошибок может быть безопасно использована с Do-Try-Catch:

do {
    let result = try someThrowingFunc() 
} catch {
    print(error)
}

Если someThrowingFunc() выдает ошибку, ошибка будет безопасно поймана в блоке catch.

Константа error вы видите в блоке catch, не была объявлена нами - она автоматически генерируется catch.

Вы также можете объявить error самостоятельно, это имеет то преимущество, что вы можете использовать ее в полезном формате, например:

do {
    let result = try someThrowingFunc()    
} catch let error as NSError {
    print(error.debugDescription)
}

Использовать эту try - это правильный способ попробовать, поймать и обработать ошибки, исходящие из функций броска.

Там также try? который поглощает ошибку:

if let result = try? someThrowingFunc() {
    // cool
} else {
    // handle the failure, but there no error information available
}

Но система обработки ошибок Swift также предоставляет возможность "принудительно попробовать" с помощью try! :

let result = try! someThrowingFunc()

Концепции, описанные в этом сообщении, также применимы здесь: если ошибка будет выброшена, приложение будет аварийно завершено.

Вы должны использовать только try! если вы можете доказать, что его результат никогда не потерпит неудачу в вашем контексте - и это очень редко.

В большинстве случаев вы будете использовать полную систему Do-Try-Catch и, если хотите, try? , в редких случаях, когда обработка ошибки не имеет значения.


Ресурсы

  • 68
    Лично я хотел бы поблагодарить вас за то, что вы приложили усилия, чтобы написать все это. Я чувствую, что это, безусловно, будет полезно как новичкам, так и экспертам, использующим swift.
  • 12
    Я рад, что вы нашли это полезным. Этот ответ - вики сообщества, так что это сотрудничество между многими людьми (пока 7)!
Показать ещё 2 комментария
49

Ответ TL; DR

За очень немногими исключениями это правило является золотым:

Избегайте использования !

Объявить переменную опциональную (?), А не неявно развернутые опции (IUO) (!)

Другими словами, скорее используйте:
var nameOfDaughter: String?

Вместо:
var nameOfDaughter: String!

Развяжите необязательную переменную, используя if let or guard let

Либо разверните переменную следующим образом:

if let nameOfDaughter = nameOfDaughter {
    print("My daughters name is: \(nameOfDaughter)")
}

Или вот так:

guard let nameOfDaughter = nameOfDaughter else { return }
print("My daughters name is: \(nameOfDaughter)")

Этот ответ должен был быть кратким, для полного понимания прочитанный принятый ответ

35

Этот вопрос появляется ВСЕ ВРЕМЯ на SO. Это одна из первых вещей, с которыми сталкиваются новые разработчики Swift.

История:

Swift использует концепцию "Optionals" для обработки значений, которые могут содержать значение, или нет. В других языках, таких как C, вы можете сохранить значение 0 в переменной, чтобы указать, что оно не содержит значения. Однако, что, если 0 является допустимым значением? Тогда вы можете использовать -1. Что, если -1 является допустимым значением? И так далее.

Опционы Swift позволяют настраивать переменную любого типа, содержащую либо допустимое значение, либо не значение.

Вы помещаете вопросительный знак после типа, когда объявляете переменную, которая означает (введите x или нет значения).

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

Необязательно необходимо "распаковать", чтобы получить значение внутри.

"!" оператор - оператор "разворота силы". Он говорит: "Поверь мне, я знаю, что делаю. Я гарантирую, что когда этот код будет запущен, переменная не будет содержать нуль". Если вы ошибаетесь, вы терпите крах.

Если вы действительно сделаете знаете, что делаете, избегайте "!" оператор разворачивания силы. Вероятно, это самый большой источник сбоев для начинающих программистов Swift.

Как иметь дело с опциями:

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

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

Вот пример необязательного связывания с нашим foo необязательным:

if let newFoo = foo //If let is called optional binding. {
  print("foo is not nil")
} else {
  print("foo is nil")
}

Обратите внимание, что переменная, которую вы определяете при использовании дополнительного биллинга, существует только (только в "видимости" ) в теле оператора if.

В качестве альтернативы вы можете использовать инструкцию охраны, которая позволяет вам выйти из вашей функции, если переменная равна nil:

func aFunc(foo: Int?) {
  guard let newFoo = input else { return }
  //For the rest of the function newFoo is a non-optional var
}

В Swift 2. Guard были добавлены утверждения Guard. Guard позволяет сохранить "золотой путь" через ваш код и избегать постоянно растущих уровней вложенных ifs, которые иногда возникают из-за использования необязательной привязки "if let".

Существует также конструкция, называемая "оператор объединения нуля". Он принимает форму "optional_var?? replacement_val". Он возвращает необязательную переменную с тем же типом, что и данные, содержащиеся в необязательном. Если опция содержит nil, она возвращает значение выражения после "??" символ.

Итак, вы можете использовать такой код:

let newFoo = foo ?? "nil" // "??" is the nil coalescing operator
print("foo = \(newFoo)")

Вы также можете использовать функцию try/catch или guard error, но, как правило, один из других методов, описанных выше, является более чистым.

EDIT:

Другой, немного более тонкий getcha с опциями - это "неявно разворачиваемые опционы". Когда мы объявляем foo, мы можем сказать:

var foo: String!

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

Итак, этот код:

var foo: String!


let upperFoo = foo.capitalizedString

Будет сбой при ссылке на свойство foo capitalizedString, даже если мы не принудительно разворачиваем foo. печать выглядит отлично, но это не так.

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

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

  • 7
    Вы должны рассмотреть возможность сделать это общедоступной вики.
  • 0
    Я не делал этого раньше. Можете ли вы указать мне инструкции, как это сделать?
Показать ещё 7 комментариев
11

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

Другой способ объявить необязательную переменную

var i : Optional<Int>

И необязательный тип - это не что иное, как перечисление с двумя случаями, i.e

 enum Optional<Wrapped> : ExpressibleByNilLiteral {
    case none 
    case some(Wrapped)
    .
    .
    .
}

Итак, чтобы назначить nil нашей переменной i. Мы можем сделать var i = Optional<Int>.none или присвоить значение, мы передадим некоторое значение var i = Optional<Int>.some(28)

Согласно быстрому, "ноль" - это отсутствие ценности. И создать экземпляр, инициализированный с помощью nil Мы должны соответствовать протоколу с именем ExpressibleByNilLiteral и отлично, если вы его догадались, только Optionals соответствует ExpressibleByNilLiteral и соответствует другим типам.

ExpressibleByNilLiteral имеет единственный метод, называемый init(nilLiteral:), который инициализирует instace с nil. Обычно вы не вызываете этот метод и, согласно быстрой документации, не рекомендуется называть этот инициализатор напрямую, когда компилятор вызывает его всякий раз, когда вы инициализируете необязательный тип с nil литералом.

Даже я должен обернуть (не каламбур) голову вокруг опций: D Счастливый Swfting All.

11

Во-первых, вы должны знать, что такое необязательное значение. Вы можете перейти на The Swift Programming Launage

для деталей.

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

Вы можете использовать if let ... или guard let ... else и т.д.

Другим способом, если вы не хотите проверять его состояние перед вашим исполнением, вы также можете использовать var buildingName = buildingName ?? "buildingName".

6

У меня была эта ошибка, когда я пытался установить значения Outlets из метода подготовки к методу segue следующим образом:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let destination = segue.destination as? DestinationVC{

        if let item = sender as? DataItem{
            // This line pops up the error
            destination.nameLabel.text = item.name
        }
    }
}

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

Итак, я решил это так:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let destination = segue.destination as? DestinationVC{

        if let item = sender as? DataItem{
            // Created this method in the destination Controller to update its outlets after it being initialized and loaded
            destination.updateView(itemData:  item)
        }
    }
}

Контроллер назначения:

// This variable to hold the data received to update the Label text after the VIEW DID LOAD
var name = ""

// Outlets
@IBOutlet weak var nameLabel: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    nameLabel.text = name
}

func updateView(itemDate: ObjectModel) {
    name = itemDate.name
}

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

  • 0
    Только не забудьте упомянуть, где вызывать updateView в контроллере назначения;)
  • 0
    @ Ахмад, хорошая мысль. Тем не менее, вызов updateView в контроллере назначения в этом случае не требуется, так как я использую переменную name для установки nameLabel.text в viewDidLoad . Но если бы мы выполняли большую настройку, то, безусловно, было бы лучше создать другую функцию, сделав это и вызывая ее из viewDidLoad .
4

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

Есть несколько сценариев, которые приводят к такой фатальной ошибке:

  1. принудительное развертывание:

    let user = someVariable!
    

    Если someVariable равен нулю, вы получите сбой. Делая принудительную развёртку, вы перекладывали ответственность компилятора на вас, в основном выполняя принудительную развёртку, вы гарантируете компилятору, что там никогда не будет значений nil. И угадайте, что произойдет, если каким-то образом значение nil заканчивается в someVariable?

    Решение? Используйте необязательную привязку (иначе if-let), выполните там обработку переменных:

    if user = someVariable {
        // do your stuff
    }
    
  2. принудительное (вниз) приведение:

    let myRectangle = someShape as! Rectangle
    

    Здесь путем принудительного приведения вы говорите компилятору больше не беспокоиться, так как у вас всегда будет экземпляр Rectangle. И пока это действительно так, вам не о чем беспокоиться. Проблемы начинаются, когда вы или ваши коллеги из проекта начинаете распространять не прямоугольные значения.

    Решение? Используйте необязательную привязку (иначе if-let), выполните там обработку переменных:

    if let myRectangle = someShape as? Rectangle {
        // yay, I have a rectangle
    }
    
  3. Неявно развернутые опции. Предположим, у вас есть следующее определение класса:

    class User {
        var name: String!
    
        init() {
            name = "(unnamed)"
        }
    
        func nicerName() {
            return "Mr/Ms " + name
        }
    }
    

    Теперь, если никто не напутает со свойством name, установив для него значение nil, тогда оно будет работать, как и ожидалось, однако, если User инициализируется из JSON, в котором отсутствует ключ name, вы получите фатальную ошибку при попытке использовать свойство,

    Решение? Не используйте их :) Если вы не уверены на 102%, что свойство всегда будет иметь значение, отличное от нуля, к моменту его использования. В большинстве случаев преобразование в необязательный или необязательный будет работать. Если вы сделаете его необязательным, компилятор также поможет вам, указав пропущенные вами пути кода, указав значение этого свойства.

  4. Не подключенные или еще не подключенные розетки. Это частный случай сценария № 3. По сути, у вас есть некоторый загруженный XIB класс, который вы хотите использовать.

    class SignInViewController: UIViewController {
    
        @IBOutlet var emailTextField: UITextField!
    }
    

    Теперь, если вы пропустили подключение к розетке из редактора XIB, приложение вылетит, как только вы захотите использовать розетку. Решение? Убедитесь, что все розетки подключены. Или использовать ? оператор на них: emailTextField?.text = "[email protected]". Или объявите выход как необязательный, хотя в этом случае компилятор заставит вас развернуть его по всему коду.

  5. Значения, поступающие из Objective-C, и которые не имеют аннотаций обнуляемости. Предположим, у нас есть следующий класс Objective-C:

    @interface MyUser: NSObject
    @property NSString *name;
    @end
    

    Теперь, если аннотации обнуляемости не заданы (явно или через NS_ASSUME_NONNULL_BEGIN/NS_ASSUME_NONNULL_END), свойство name будет импортировано в Swift как String! (IUO - неявно развернутый необязательный). Как только какой-то swift-код захочет использовать это значение, он потерпит крах, если name равно nil.

    Решение? Добавьте аннулируемые аннотации к вашему коду Objective-C. Однако остерегайтесь, когда компилятор Objective-C немного допустим, когда дело доходит до обнуляемости, вы можете получить нулевые значения, даже если вы явно пометили их как nonnull.

0

Позвольте понять это глубокое погружение, одна из самых важных вещей в Swift Language.

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

Как правило, переменная/константа не может иметь значение nil, если явно не указано, что переменная может иметь значение nil. Например

let a : Int = nil

не будет компилироваться.

Зачем нужны дополнительные

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

Как объявить опционные

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

var a : Int?

Вы можете иметь опцию любого типа, включая ваши пользовательские типы.

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

Хотя для использования опциональных вам не нужно знать, как они реализованы в Swift. Но всегда лучше знать это, это помогает расширить ваше понимание. Итак, давайте исследуем это

Как реализованы дополнительные функции в Swift?

Вы используете необязательный тип всякий раз, когда используете необязательные значения. Тип Optional - это перечисление с двумя падежами. Optional.none эквивалентен литералу nil. Optional.some(Wrapped) хранит упакованное значение. Например:

let number: Int? = Optional.some(56)
let noNumber: Int? = Optional.none
print(noNumber == nil)    // Prints "true"
The definition of Optional is like this in Swift

public enum Optional<Wrapped> : ExpressibleByNilLiteral {

/// The absence of a value.
///
/// In code, the absence of a value is typically written using the 'nil'
/// literal rather than the explicit '.none' enumeration case.
case none

/// The presence of a value, stored as 'Wrapped'.
case some(Wrapped)
}

Выше приведено только частичное определение Optional (показаны только случаи, а не методы).

Итак, увидев определение, вы знаете, что мы можем объявить необязательный Int как

//long form to delcare an optional Int
let number: Optional<Int>

Это просто длинная форма. Вы должны избегать использования его для удобства чтения. Вы всегда должны объявлять свои дополнительные опции с завершающим вопросительным знаком (?) С типом.

Что-то о нуле в Swift

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

Один вопрос перед закрытием темы. Рассмотрим следующий фрагмент кода

var a : Int? = 5
var b =  a
b = nil ///IS IT OK?

Каково ваше предположение? Можно ли здесь назначить ноль? ДА!. Поскольку b присваивается переменная, которая сама является необязательной Int, поэтому выведенный тип для b также будет необязательным Int.

Так что это то, что необязательно в Swift.

Источник: https://swiftwithsadiq.wordpress.com/2017/08/01/optionals-in-swift/

-1

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

Подумайте о следующем коде: он компилируется без ошибок/предупреждений:

c1.address.city = c3.address.city

Тем не менее, во время выполнения он выдает следующую ошибку: Неустранимая ошибка: неожиданно обнаружен ноль при развертывании необязательного значения

Можете ли вы сказать мне, какой объект nil?

Вы не можете!

Полный код будет:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        var c1 = NormalContact()
        let c3 = BadContact()

        c1.address.city = c3.address.city // compiler hides the truth from you and then you sudden get a crash
    }
}

struct NormalContact {
    var address : Address = Address(city: "defaultCity")
}

struct BadContact {
    var address : Address!
}

struct Address {
    var city : String
}

Короче говоря, используя var address: Address! вы скрываете возможность того, что переменная может быть nil от других читателей. И когда он выходит из строя, вы говорите: "Какого черта?! Мой address не является обязательным, так почему я рушаюсь?!".

Следовательно лучше написать так:

c1.address.city = c2.address!.city  // ERROR:  Fatal error: Unexpectedly found nil while unwrapping an Optional value 

Можете ли вы сказать мне, что это был за nil?

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

Полный код будет:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        var c1 = NormalContact()
        let c2 = GoodContact()

        c1.address.city = c2.address!.city
        c1.address.city = c2.address?.city // not compile-able. No deceiving by the compiler
        c1.address.city = c2.address.city // not compile-able. No deceiving by the compiler
        if let city = c2.address?.city {  // safest approach. But that not what I'm talking about here. 
            c1.address.city = city
        }

    }
}

struct NormalContact {
    var address : Address = Address(city: "defaultCity")
}

struct GoodContact {
    var address : Address?
}

struct Address {
    var city : String
}
-3

фатальная ошибка: неожиданно найден nil при развертывании необязательного значения

В нем говорится, что вы используете переменную, значение которой не инициализировано или не задано nil.

Например

var tempVar: String?
print(tempVar!)

tempVar не инициализирован, поэтому в этом случае произойдет сбой приложений, поэтому вам нужно использовать

print(tempVar ?? "") 

Дополнительную информацию см. В разделе "Дополнительная цепочка"

-4

Если вы столкнулись с этой ошибкой при работе с "tableview", вы должны проверить "идентификатор ячейки"

Изображение 1666

Ещё вопросы

Сообщество Overcoder
Наверх
Меню