Моя программа Swift аварийно завершает работу с EXC_BAD_INSTRUCTION
одной из следующих подобных ошибок. Что означает эта ошибка и как ее исправить?
Неустранимая ошибка: неожиданно обнаружен ноль при развертывании необязательного значения
или
Неустранимая ошибка: неожиданно обнаружен ноль при неявном развертывании необязательного значения
Этот пост предназначен для сбора ответов на «неожиданно найденные ноль» проблемы, чтобы они не были разбросаны и их было сложно найти. Не стесняйтесь добавлять свой собственный ответ или редактировать существующий вики-ответ.
swift
exception
error-handling
pkamb
источник
источник
Ответы:
Этот ответ вики сообщества . Если вы чувствуете, что это может быть сделано лучше, не стесняйтесь редактировать это !
Фон: что необязательно?
В Swift
Optional
- это универсальный тип, который может содержать значение (любого типа) или вообще не иметь значения.Во многих других языках программирования определенное значение «страж» часто используется для указания на отсутствие значения . Например,
nil
в Objective-C ( нулевой указатель ) указывает на отсутствие объекта. Но это становится более сложным при работе с примитивными типами - следует-1
использовать, чтобы указать отсутствие целого числа, или, возможноINT_MIN
, или какого-то другого целого числа? Если какое-либо конкретное значение выбрано для обозначения «нет целого числа», это означает, что оно больше не может рассматриваться как допустимое значение.Swift - это типобезопасный язык, который означает, что он поможет вам понять типы значений, с которыми может работать ваш код. Если часть вашего кода ожидает строку, тип безопасности не позволяет вам передать ему Int по ошибке.
В Swift любой тип можно сделать необязательным . Необязательное значение может принимать любое значение из исходного типа или специальное значение
nil
.Необязательные параметры определяются с
?
суффиксом типа:Отсутствие значения в опциональном обозначается
nil
:(Обратите внимание, что это
nil
не то же самое, чтоnil
в Objective-C. В Objective-Cnil
- отсутствие действительного указателя объекта ; в Swift Optional не ограничиваются объектами / ссылочными типами. Optional ведет себя подобно Haskell Maybe .)Почему я получил « фатальную ошибку: неожиданно обнаружил ноль при развертывании необязательного значения »?
Чтобы получить доступ к необязательному значению (если оно вообще есть), вам необходимо развернуть его. Необязательное значение может быть развернуто безопасно или принудительно. Если вы принудительно развернете необязательный параметр, у которого не было значения, ваша программа вылетит с сообщением, приведенным выше.
Xcode покажет вам сбой, выделив строку кода. Проблема возникает на этой линии.
Этот сбой может произойти с двумя различными видами принудительного развертывания:
1. Явное развертывание силы
Это делается с
!
оператором по желанию. Например:Как
anOptionalString
иnil
здесь, вы получите сбой на линии, где вы принудительно разверните его.2. Неявно развернутые необязательные
Они определены с
!
, а не?
после типа.Предполагается, что эти опции содержат значение. Поэтому всякий раз, когда вы получаете доступ к неявно развернутому необязательному файлу, он будет автоматически развернут для вас. Если он не содержит значения, он потерпит крах.
Чтобы определить, какая переменная вызвала сбой, вы можете удерживать нажатой ⌥, чтобы показать определение, где вы можете найти необязательный тип.
В частности, IBOutlets обычно являются неявно развернутыми опциями. Это потому, что ваш xib или раскадровка свяжут розетки во время выполнения после инициализации. Поэтому вы должны убедиться, что у вас нет доступа к розеткам до их загрузки. Вы также должны проверить правильность соединений в вашем файле раскадровки / xib, в противном случае значения будут
nil
во время выполнения и, следовательно, сбой, когда они неявно развернуты. , При исправлении соединений попробуйте удалить строки кода, которые определяют ваши розетки, а затем снова подключить их.Когда я должен когда-либо принудительно развернуть Дополнительный?
Явная распаковка силы
Как правило, вы никогда не должны явно развертывать опционально с
!
оператором. Могут быть случаи, когда использование!
приемлемо, но вы должны использовать его только в том случае, если вы на 100% уверены, что необязательный параметр содержит значение.Хотя может быть случай, когда вы можете использовать принудительное развертывание, как вы знаете, для факта, что необязательный параметр содержит значение - нет ни одного места, где вы не можете безопасно развернуть это необязательное.
Неявно развернутые необязательные
Эти переменные разработаны таким образом, что вы можете отложить их назначение до следующего момента в вашем коде. Это ваша ответственность , чтобы убедиться , что они имеют значение , прежде чем получить доступ к ним. Однако, поскольку они включают принудительное развертывание, они по-прежнему небезопасны - поскольку они предполагают, что ваше значение не равно нулю, даже если присвоение nil допустимо.
Вы должны использовать только неявно развернутые опции как последнее средство . Если вы можете использовать переменную lazy или предоставить значение по умолчанию для переменной - вы должны сделать это вместо использования неявно развернутого необязательного параметра.
Тем не менее, есть несколько сценариев, в которых неявно развернутые дополнительные компоненты полезны , и вы все еще можете использовать различные способы их безопасного развертывания, как указано ниже, но вы всегда должны использовать их с должной осторожностью.
Как я могу безопасно иметь дело с Опциональными?
Самый простой способ проверить, содержит ли необязательное значение значение, - сравнить его с
nil
.Тем не менее, в 99,9% случаев при работе с дополнительными компонентами вы фактически захотите получить доступ к содержащемуся в нем значению, если оно вообще содержится. Для этого вы можете использовать Optional Binding .
Дополнительное связывание
Опциональное связывание позволяет вам проверить, содержит ли опциональное значение значение, и позволяет назначить развернутое значение новой переменной или константе. Он использует синтаксис
if let x = anOptional {...}
илиif var x = anOptional {...}
, в зависимости от того, нужно ли вам изменить значение новой переменной после ее привязки.Например:
Это сначала проверяет, что необязательное содержит значение. Если это так , тогда значение unwrapped присваивается новой переменной (
number
), которую вы можете свободно использовать, как если бы она была необязательной. Если необязательный параметр не содержит значения, то будет вызываться предложение else, как и следовало ожидать.Преимущество необязательного связывания заключается в том, что вы можете развернуть несколько необязательных элементов одновременно. Вы можете просто отделить утверждения запятой. Оператор будет успешным, если все дополнительные параметры были развернуты.
Еще один полезный трюк заключается в том, что вы можете также использовать запятые, чтобы проверить определенное условие для значения после его разворачивания.
Единственная особенность использования необязательного связывания в операторе if заключается в том, что вы можете получить доступ только к развернутому значению в пределах оператора. Если вам нужен доступ к значению вне области действия оператора, вы можете использовать защитный оператор .
Оператор охранника позволяет определить условие для успеха - и текущая область будет только продолжать выполнение , если это условие выполнено. Они определены с помощью синтаксиса
guard condition else {...}
.Итак, чтобы использовать их с необязательной привязкой, вы можете сделать это:
(Обратите внимание, что в теле защиты вы должны использовать один из операторов передачи управления для выхода из области выполняемого в данный момент кода).
Если
anOptionalInt
содержит значение, оно будет развернуто и присвоено новойnumber
константе. Код после охраны продолжит выполнение. Если оно не содержит значения - охранник выполнит код в скобках, что приведет к передаче управления, так что код сразу после этого не будет выполнен.Реальная изящная вещь в операторах guard - это то, что теперь развернутое значение доступно для использования в коде, который следует за оператором (поскольку мы знаем, что будущий код может выполняться только в том случае, если необязательное значение имеет значение). Это отлично подходит для устранения «пирамид гибели», созданных путем вложения нескольких операторов if.
Например:
Охранники также поддерживают те же хитрые приемы, которые поддерживал оператор if, такие как одновременное развертывание нескольких дополнительных опций и использование
where
предложения.Используете ли вы оператор if или guard, полностью зависит от того, требует ли какой-либо будущий код необязательного значения.
Оператор слияния
Коалесцентный Оператор Nil это отличный вариант стенографии в тройном условном операторе , в первую очередь предназначен для преобразования в УСТРОЙСТВО , не являющиеся дополнительные опции. Он имеет синтаксис
a ?? b
, гдеa
это необязательный тип иb
тот же тип, что иa
(хотя обычно не необязательный).По сути, он позволяет вам сказать: «Если
a
содержит значение, разверните его. Если этого не произойдет, тогда вернитесьb
вместо этого ». Например, вы можете использовать это так:Это определит
number
константуInt
типа, которая будет либо содержать значениеanOptionalInt
, если оно содержит значение, либо0
иным образом.Это просто сокращение для:
Опциональная цепочка
Вы можете использовать Optional Chaining , чтобы вызвать метод или получить доступ к свойству по желанию. Это просто делается путем добавления суффикса к имени переменной при его
?
использовании.Например, допустим, у нас есть переменная
foo
типа необязательныйFoo
экземпляр.Если мы хотим вызвать метод,
foo
который ничего не возвращает, мы можем просто сделать:Если
foo
содержит значение, этот метод будет вызван для него. Если этого не произойдет, ничего плохого не произойдет - код просто продолжит выполнение.(Это похоже на отправку сообщений
nil
в Objective-C)Поэтому его также можно использовать для установки свойств, а также для вызова методов. Например:
Опять же, ничего плохого здесь не произойдет, если
foo
естьnil
. Ваш код просто продолжит выполнение.Еще одна хитрость, которую позволяет делать необязательное связывание, - это проверка успешности установки свойства или вызова метода. Вы можете сделать это, сравнивая возвращаемое значение с
nil
.(Это потому, что необязательное значение будет возвращаться,
Void?
а неVoid
для метода, который ничего не возвращает)Например:
Однако все становится немного сложнее при попытке получить доступ к свойствам или вызвать методы, которые возвращают значение. Поскольку
foo
это необязательно, все, что возвращается из него, также будет необязательным. Чтобы справиться с этим, вы можете либо развернуть дополнительные параметры, возвращаемые с помощью одного из перечисленных выше методов, либо развернутьfoo
себя перед доступом к методам или вызову методов, возвращающих значения.Кроме того, как следует из названия, вы можете «связать» эти утверждения вместе. Это означает, что если
foo
есть необязательное свойствоbaz
, которое имеет свойствоqux
- вы можете написать следующее:Опять же, поскольку
foo
иbaz
являются необязательными, возвращаемое значениеqux
всегда будет необязательным независимо от тогоqux
, является ли оно необязательным.map
а такжеflatMap
Часто недогружена функция с опциями является возможностью использовать
map
иflatMap
функцию. Это позволяет вам применять необязательные преобразования к необязательным переменным. Если необязательный параметр имеет значение, вы можете применить к нему данное преобразование. Если оно не имеет значения, оно останетсяnil
.Например, допустим, у вас есть необязательная строка:
Применяя
map
к ней функцию - мы можем использовать этуstringByAppendingString
функцию, чтобы соединить ее с другой строкой.Поскольку
stringByAppendingString
принимает необязательный строковый аргумент, мы не можем напрямую ввести нашу необязательную строку. Однако, используяmap
, мы можем использовать allowstringByAppendingString
для использования, еслиanOptionalString
имеет значение.Например:
Однако, если
anOptionalString
не имеет значения,map
вернетсяnil
. Например:flatMap
работает аналогичноmap
, за исключением того, что позволяет возвращать другой необязательный элемент из тела замыкания. Это означает, что вы можете вводить необязательный в процесс, который требует не необязательного ввода, но может сам выводить необязательный.try!
Система обработки ошибок Swift может безопасно использоваться с Do-Try-Catch :
Если
someThrowingFunc()
выдает ошибку, ошибка будет благополучно поймана вcatch
блоке.error
Константа вы видите вcatch
блоке не была объявлена нами - это автоматически генерируетсяcatch
.Вы также можете заявить о
error
себе, у него есть то преимущество, что вы можете преобразовать его в полезный формат, например:Использование
try
этого способа является правильным способом попытаться поймать и обработать ошибки, возникающие в результате бросания функций.Там также,
try?
который поглощает ошибку:Но система обработки ошибок Swift также предоставляет способ «принудительной попытки» с помощью
try!
:Понятия, описанные в этом посте, также применимы и здесь: если выдается ошибка, приложение вылетает.
Вы должны использовать его только в том
try!
случае, если сможете доказать, что его результат никогда не потерпит неудачу в вашем контексте - и это очень редко.Большую часть времени вы будете использовать полную систему Do-Try-Catch - и дополнительную
try?
, в тех редких случаях, когда обработка ошибки не важна.Ресурсы
источник
compactMap()
вместоflatMap()
.TL; DR ответ
За очень немногими исключениями это правило является золотым:
Избегайте использования
!
Объявите переменную
?
option ( ), а не неявно развернутые optionals (IUO) (!
)Другими словами, лучше использовать:
var nameOfDaughter: String?
Вместо:
var nameOfDaughter: String!
Разверните необязательную переменную, используя
if let
илиguard let
Либо разверните переменную, как это:
Или вот так:
Этот ответ должен был быть кратким, для полного понимания прочитайте принятый ответ
Ресурсы
источник
Этот вопрос возникает все время на SO. Это одна из первых вещей, с которыми борются новые разработчики Swift.
Фон:
Swift использует концепцию «Optionals» для работы со значениями, которые могут содержать значение или нет. В других языках, таких как C, вы можете хранить значение 0 в переменной, чтобы указать, что оно не содержит значения. Однако что, если 0 является допустимым значением? Тогда вы можете использовать -1. Что если -1 является допустимым значением? И так далее.
Опции Swift позволяют вам установить переменную любого типа, которая будет содержать либо действительное значение, либо отсутствие значения.
Вы ставите вопросительный знак после типа, когда объявляете значение переменной (тип x или нет значения).
Необязательным на самом деле является контейнер, который содержит либо переменную заданного типа, либо ничего.
Необязательный должен быть "развернут", чтобы получить значение внутри.
"!" оператор является оператором "развернуть силой". Он говорит: «Поверьте мне. Я знаю, что я делаю. Я гарантирую, что при запуске этого кода переменная не будет содержать ноль». Если вы не правы, вы терпите крах.
Если вы действительно не знаете, что делаете, избегайте "!" Принудительно развернуть оператор. Вероятно, это самый большой источник сбоев для начинающих программистов Swift.
Как бороться с опционами:
Есть много других способов иметь дело с опциями, которые безопаснее. Вот некоторые (не исчерпывающий список)
Вы можете использовать «необязательное связывание» или «если позволено», чтобы сказать «если это необязательное содержит значение, сохраните это значение в новой, необязательной переменной. Если необязательное не содержит значения, пропустите основную часть этого оператора if». ».
Вот пример необязательного связывания с нашим
foo
необязательным:Обратите внимание, что переменная, которую вы определяете при использовании необязательного связывания, существует (находится только в области видимости) в теле оператора if.
В качестве альтернативы вы можете использовать оператор guard, который позволяет вам выйти из функции, если переменная равна nil:
Операторы Guard были добавлены в Swift 2. Guard позволяет вам сохранять «золотой путь» в вашем коде и избегать постоянно увеличивающихся уровней вложенных if, которые иногда возникают в результате использования необязательного связывания «if let».
Существует также конструкция, называемая «оператор слияния ноль». Он принимает форму «необязательный_вар», «замена_вал». Он возвращает необязательную переменную того же типа, что и данные, содержащиеся в необязательной переменной. Если необязательный параметр содержит nil, он возвращает значение выражения после «??» символ.
Таким образом, вы можете использовать такой код:
Вы также можете использовать try / catch или обработку ошибок guard, но, как правило, один из других методов выше - чище.
РЕДАКТИРОВАТЬ:
Другой, несколько более тонкий хитрость с опциями - это «неявно развернутые опционы». Когда мы объявляем foo, мы можем сказать:
В этом случае foo все еще не обязателен, но вам не нужно разворачивать его, чтобы ссылаться на него. Это означает, что каждый раз, когда вы пытаетесь сослаться на foo, вы теряете значение, если оно равно нулю.
Итак, этот код:
Сбой при ссылке на свойство foo capitalizedString, даже если мы не распаковываем foo принудительно. отпечаток выглядит хорошо, но это не так.
Таким образом, вы хотите быть очень осторожным с неявно развернутыми опциями. (и, возможно, даже избегайте их полностью, пока у вас не будет четкого понимания дополнительных возможностей.)
Итог: когда вы впервые изучаете Swift, сделайте вид «!» характер не является частью языка. Это может привести к неприятностям.
источник
guard
пунктах. Там нет ничего о использованииif var
которого так же, как конструкцияif let
. В предложениях нет ничего такого, оwhere
чем стоит упомянуть, когда мы говорим оif let
связывании (во многих случаях это устраняет целый слой вложенности). Там нет ничего о необязательной цепочки.Поскольку приведенные выше ответы четко объясняют, как безопасно играть с Optionals. Я постараюсь объяснить, что на самом деле опционально быстро.
Еще один способ объявить необязательную переменную
var i : Optional<Int>
А необязательный тип - это не что иное, как перечисление с двумя падежами, т.е.
Таким образом, чтобы присвоить ноль нашей переменной «я». Мы можем сделать
var i = Optional<Int>.none
или присвоить значение, мы передадим некоторое значениеvar i = Optional<Int>.some(28)
Согласно Свифту, «ноль» - это отсутствие стоимости. И для создания экземпляра, инициализированного с помощью.
nil
Мы должны соответствовать протоколу, который называетсяExpressibleByNilLiteral
и отлично, если вы догадались, толькоOptionals
соответствоватьExpressibleByNilLiteral
и соответствовать другим типам не рекомендуется.ExpressibleByNilLiteral
имеет единственный вызванный метод,init(nilLiteral:)
который инициализирует instace с nil. Вы обычно не вызываете этот метод, и в соответствии с быстрой документацией не рекомендуется вызывать этот инициализатор напрямую, так как компилятор вызывает его всякий раз, когда вы инициализируете необязательный типnil
литералом.Даже я должен обернуть (не каламбур) голову вокруг Факультативного: D Happy Swfting All .
источник
Во-первых, вы должны знать, что такое необязательное значение. Вы можете перейти на язык программирования Swift для деталей.
Во-вторых, вы должны знать, что необязательное значение имеет два статуса. Один - полное значение, а другой - нулевое значение. Поэтому, прежде чем реализовать необязательное значение, вы должны проверить, в каком оно состоянии.
Вы можете использовать
if let ...
илиguard let ... else
и так далее.Еще один способ, если вы не хотите проверять состояние переменной перед реализацией, вы также можете использовать
var buildingName = buildingName ?? "buildingName"
вместо этого.источник
Однажды у меня была эта ошибка, когда я пытался установить свои значения Outlets из метода подготовки к переходу следующим образом:
Затем я обнаружил, что не могу установить значения выходов контроллера назначения, потому что контроллер еще не загружен или не инициализирован.
Итак, я решил это так:
Конечный контроллер:
Я надеюсь, что этот ответ поможет любому, кто столкнулся с той же проблемой, поскольку я обнаружил, что отмеченный ответ является отличным ресурсом для понимания дополнительных возможностей и того, как они работают, но сам не решил эту проблему напрямую.
источник
updateView
в контроллере назначения;)updateView
контроллера in в этом случае не требуется, так как я используюname
переменную для установкиnameLabel.text
вviewDidLoad
. Но если бы мы выполняли большую настройку, то, безусловно, было бы лучше создать другую функцию, сделав это и вызвав ееviewDidLoad
вместо этого.По сути, вы пытались использовать значение nil в тех местах, где Swift допускает только значения, отличные от nil, сказав, что компилятор доверяет вам, что там никогда не будет значения nil, что позволяет вашему приложению компилироваться.
Есть несколько сценариев, которые приводят к такой фатальной ошибке:
принудительное развертывание:
Если
someVariable
ноль, то вы получите сбой. Делая принудительную развёртку, вы перекладывали ответственность за проверку на ноль от компилятора к вам, в основном, выполняя принудительную развёртку, вы гарантируете компилятору, что там никогда не будет нулевых значений. И угадайте, что произойдет, если каким-то образом значение nil закончитсяsomeVariable
?Решение? Используйте необязательное связывание (иначе if-let), выполните обработку переменной там:
принудительное (вниз) приведение:
Здесь путем принудительного приведения вы говорите компилятору больше не беспокоиться, так как у вас всегда будет
Rectangle
экземпляр. И пока это действительно так, вам не о чем беспокоиться. Проблемы начинаются, когда вы или ваши коллеги из проекта начинаете распространять не прямоугольные значения.Решение? Используйте необязательное связывание (иначе if-let), выполните обработку переменной там:
Неявно развернутые опции. Предположим, у вас есть следующее определение класса:
Теперь, если никто не запутался со
name
свойством, установив его в значениеnil
, то оно работает как положено, однако, еслиUser
инициализируется из JSON, в котором отсутствуетname
ключ, вы получите фатальную ошибку при попытке использовать свойство.Решение? Не используйте их :) Если вы не уверены на 102%, что свойство всегда будет иметь значение, отличное от нуля, к моменту его использования. В большинстве случаев преобразование в необязательный или необязательный будет работать. Если вы сделаете его необязательным, компилятор также поможет вам, указав пропущенные вами пути кода, указав значение этого свойства.
Не подключенные или еще не подключенные розетки. Это частный случай сценария № 3. По сути, у вас есть некоторый загруженный XIB класс, который вы хотите использовать.
Теперь, если вы пропустили подключение к розетке из редактора XIB, приложение завершит работу, как только вы захотите использовать розетку. Решение? Убедитесь, что все розетки подключены. Или используйте
?
оператор на них:emailTextField?.text = "my@email.com"
. Или объявите выход как опциональный, хотя в этом случае компилятор заставит вас развернуть его по всему коду.Значения, поступающие из Objective-C, и которые не имеют аннотаций обнуляемости. Предположим, у нас есть следующий класс Objective-C:
Теперь, если аннотации обнуляемости не указаны (явно или через
NS_ASSUME_NONNULL_BEGIN
/NS_ASSUME_NONNULL_END
), тогдаname
свойство будет импортировано в Swift какString!
(IUO - неявно развернутый необязательно). Как только какой-то swift-код захочет использовать это значение, он потерпит неудачу, еслиname
равен нулю.Решение? Добавьте аннулируемые аннотации к вашему коду Objective-C. Однако остерегайтесь, компилятор Objective-C немного допустим, когда дело доходит до обнуляемости, вы можете получить нулевые значения, даже если вы явно пометили их как
nonnull
.источник
Это более важный комментарий, и поэтому неявно развернутые дополнительные параметры могут быть обманчивы, когда речь идет об отладке
nil
значений.Подумайте о следующем коде: он компилируется без ошибок / предупреждений:
Тем не менее, во время выполнения он выдает следующую ошибку: Неустранимая ошибка: неожиданно обнаружен ноль при развертывании необязательного значения
Можете ли вы сказать мне, какой объект
nil
?Вы не можете!
Полный код будет:
Короче говоря,
var address : Address!
вы скрываете, что переменная может бытьnil
от других читателей. И когда он падает, ты как "что, черт возьми ?! Мойaddress
не является обязательным, так почему я сбой?!.Поэтому лучше написать так:
Можете ли вы сказать мне, что это был за объект
nil
?На этот раз код стал более понятным для вас. Вы можете рационализировать и думать, что, скорее всего, это
address
параметр, который был принудительно развернут.Полный код будет:
источник
Ошибки
EXC_BAD_INSTRUCTION
иfatal error: unexpectedly found nil while implicitly unwrapping an Optional value
появляются чаще всего, когда вы объявили@IBOutlet
, но не подключены к раскадровке .Вы также должны узнать о том, как работают дополнительные устройства , упомянутые в других ответах, но это единственный случай, который мне больше всего кажется.
источник
@IBOutlet
причина этой ошибки иметь фатальную ошибку: неожиданно найден ноль при неявном развертывании версии ошибки с дополнительным значением ?Если вы получаете эту ошибку в CollectionView, попробуйте создать файл CustomCell и Custom xib.
добавить этот код в ViewDidLoad () на mainVC.
источник
Я столкнулся с этой ошибкой при переходе от контроллера табличного представления к контроллеру представления, потому что я забыл указать имя пользовательского класса для контроллера представления в основной раскадровке.
Что-то простое, что стоит проверить, если все остальное выглядит хорошо
источник