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

Aug 24 2015

Моя программа Swift вылетает EXC_BAD_INSTRUCTIONиз-за одной из следующих похожих ошибок. Что означает эта ошибка и как ее исправить?

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

или

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


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

Ответы

685 Hamish Aug 24 2015 at 02:10

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

Предыстория: что необязательно?

В Swift Optional<Wrapped>это тип параметра : он может содержать любое значение из исходного («обернутого») типа или вообще не иметь значения (специальное значение nil). Необязательное значение необходимо развернуть, прежде чем его можно будет использовать.

Необязательный - это универсальный тип , что означает, что Optional<Int>и Optional<String>являются разными типами - тип внутри <>называется обернутым типом. Под капотом Optional - это перечисление с двумя регистрами: .some(Wrapped)и .none, где .noneэквивалентно nil.

Необязательные параметры могут быть объявлены с использованием именованного типа Optional<T>или (чаще всего) в виде сокращения с ?суффиксом.

var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int?  // `nil` is the default when no value is provided
var aVerboseOptionalInt: Optional<Int>  // equivalent to `Int?`

anOptionalInt = nil // now this variable contains nil instead of an integer

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

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

Некоторые другие языки программирования также имеют общие типы параметров : например, Maybe в Haskell, option в Rust и optional в C ++ 17.

В языках программирования без типов опций конкретное «контрольное» значение часто используется для обозначения отсутствия допустимого значения. В Objective-C, например, nil( нулевой указатель ) означает отсутствие объекта. Для примитивных типов, таких как intнулевой указатель, использовать нельзя, поэтому вам потребуется либо отдельная переменная (например, value: Intи isValid: Bool), либо назначенное контрольное значение (например, -1или INT_MIN). Эти подходы подвержены ошибкам, потому что легко забыть проверить isValidили проверить контрольное значение. Кроме того, если определенное значение выбрано в качестве дозорного, это означает, что оно больше не может рассматриваться как допустимое значение.

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


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

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

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

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

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

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

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

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

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

2. Неявно развернутые опции

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

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

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

print(optionalDouble) // <- CRASH

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

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

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


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

Явное принудительное разворачивание

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

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

Неявно развернутые опции

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

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

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


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

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

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

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

Необязательная привязка

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

Например:

if let number = anOptionalInt {
    print("Contains a value! It is \(number)!")
} else {
    print("Doesn’t 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, it’s: \(text)")
} else {
    print("One or more of the optionals don’t contain a value")
}

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

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

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

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

Итак, чтобы использовать их с необязательной привязкой, вы можете сделать это:

guard let number = anOptionalInt else {
    return
}

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

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

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

Например:

guard let number = anOptionalInt else {
    return
}

print("anOptionalInt contains a value, and it’s: \(number)!")

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

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

Оператор Nil Coalescing

Коалесцентный Оператор Nil это отличный вариант стенографии в тройном условном операторе , в первую очередь предназначен для преобразования в УСТРОЙСТВО , не являющиеся дополнительные опции. Он имеет синтаксис a ?? b, где a- необязательный тип и bтот же тип a(хотя обычно необязательный).

По сути, он позволяет вам сказать: «Если aсодержит значение, разверните его. Если нет, то возвращайся b». Например, вы можете использовать это так:

let number = anOptionalInt ?? 0

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

Это просто сокращение для:

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

Дополнительная цепочка

Вы можете использовать Optional Chaining , чтобы вызвать метод или получить доступ к свойству на optional. Это просто делается путем добавления к имени переменной суффикса ?при ее использовании.

Например, предположим, что у нас есть переменная 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 wasn’t set successfully")
}

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

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

let optionalQux = foo?.baz?.qux

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

map а также flatMap

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

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

let anOptionalString:String?

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

try!

Систему обработки ошибок Swift можно безопасно использовать с 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's no error information available
}

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

let result = try! someThrowingFunc()

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

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

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


Ресурсы

65 Sajjon Dec 19 2016 at 20:26

TL; DR ответ

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

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

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

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

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

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

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

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

Или вот так:

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

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


Ресурсы

40 DuncanC Feb 28 2016 at 21:37

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

Задний план:

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

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

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

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

Необязательный параметр необходимо «развернуть», чтобы получить значение внутри.

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

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

Как работать с опционами:

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

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

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

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

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

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

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 позволяет сохранить «золотой путь» в коде и избежать постоянно растущих уровней вложенных if, которые иногда возникают в результате использования необязательной привязки «if let».

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

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

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

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

РЕДАКТИРОВАТЬ:

Еще одна, немного более тонкая проблема с необязательными параметрами - это «неявно развернутые необязательные параметры. Когда мы объявляем foo, мы могли бы сказать:

var foo: String!

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

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

var foo: String!


let upperFoo = foo.capitalizedString

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

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

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

13 Tharzeez Nov 17 2017 at 23:15

Поскольку приведенные выше ответы четко объясняют, как безопасно играть с Optionals. Я постараюсь быстро объяснить, что такое Optionals.

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

var i : Optional<Int>

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

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

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

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

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

Даже мне приходится оборачиваться (без каламбура) вокруг Optionals: D Happy Swfting All .

12 QiunCheng Jul 30 2016 at 14:18

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

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

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

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

8 Wissa Apr 13 2018 at 21:06

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

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's 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
}

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

7 Cristik Sep 19 2018 at 13:04

В основном вы пытались использовать значение 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
    

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

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

2 Honey Nov 01 2018 at 11:39

Это более важный комментарий, и именно поэтому неявно развернутые опции могут быть обманчивыми, когда дело доходит до 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's 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
}
2 AleMohamad May 21 2019 at 00:34

Ошибки EXC_BAD_INSTRUCTIONи fatal error: unexpectedly found nil while implicitly unwrapping an Optional valueпоявляются чаще всего, когда вы объявили @IBOutlet, но не подключены к раскадровке .

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

2 ShigaSuresh Feb 13 2020 at 01:16

Если вы получаете эту ошибку в CollectionView, попробуйте также создать файл CustomCell и Custom xib.

добавьте этот код в ViewDidLoad () на mainVC.

    let nib = UINib(nibName: "CustomnibName", bundle: nil)
    self.collectionView.register(nib, forCellWithReuseIdentifier: "cell")
MikeVolmar Sep 26 2019 at 22:11

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

Что-то простое, что стоит проверить, все ли в порядке