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

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

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


This post is intended to collect answers to "unexpectedly found nil" issues, so that they are not scattered and hard to find. Feel free to add your own answer or edit the existing wiki answer.

+385
источник поделиться
15 ответов

This answer is community wiki. If you feel it could be made better, feel free to edit it!

Справочная информация: что является дополнительным?

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

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

Swift - это типобезопасный язык, который означает, что он помогает вам понять типы значений, с которыми может работать ваш код. Если часть вашего кода ожидает строку, тип безопасности не позволяет вам передать ему 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

(Note that this [TG49] is not the same as the [TG410] in Objective-C. In Objective-C, [TG411] is the absence of a valid object pointer; in Swift, Optionals are not restricted to objects/reference types. Optional behaves similarly to Haskell Maybe.)


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

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

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

crashed line

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

1. Явная распаковка силы

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

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

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

2. Неявно развернутые необязательные

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

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

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

print(optionalDouble) // <- CRASH

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

В частности, 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 let number = anOptionalInt else {
    return
}

(Note that within the guard body, you must use one of the control transfer statements in order to exit the scope of the currently executing code).

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

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

Например:

guard let number = anOptionalInt else {
    return
}

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

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

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

Оператор слияния ноль

Оператор слияния Nil - это изящная сокращенная версия троичного условного оператора, в основном предназначенная для преобразования необязательных необязательных параметров. Он имеет синтаксис 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 содержит значение, этот метод будет вызван для него. Если этого не произойдет, ничего плохого не произойдет - код просто продолжит выполнение.

(This is similar behaviour to sending messages to [TG456] in Objective-C)

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

foo?.bar = Bar()

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

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

(This is because an optional value will return [TG461] rather than [TG462] on a method that doesnt return anything)

Например:

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

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

let anOptionalString:String?

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

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

Систему быстрой обработки ошибок можно безопасно использовать с 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?, в тех редких случаях, когда обработка ошибки не важна.


Ресурсы

+628
источник

Ответ 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)")

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

+62
источник
другие ответы

Связанные вопросы


Похожие вопросы

Этот вопрос появляется ВСЕ ВРЕМЯ на 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, притворитесь "!" символ не является частью языка. Вероятно, вы попадете в неприятности.

+39
источник

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

для деталей.

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

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

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

+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
источник

У меня была эта ошибка, когда я пытался установить значения 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
}

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

+6
источник

По сути, вы пытались использовать значение 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.

+6
источник

Позвольте понять это глубокое погружение, одна из самых важных вещей в 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/

0
источник

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

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

0
источник

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

Что-то простое, что стоит проверить, если все остальное выглядит хорошо

0
источник

Это более важный комментарий, и поэтому неявно развернутые дополнительные параметры могут быть обманчивы, когда дело доходит до отладки 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
}
-1
источник

Потратьте часы на это, может оказаться полезным для кого-то еще: Для меня розетки были равны нулю (таким образом, выдает ошибку: неожиданно обнаружил, что ноль при развертывании необязательного значения) из-за этого: IBOutlet равен нулю, но он связан в раскадровке Swift

-1
источник

Swift 5 и выше (опционально и опционально)

class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if let buildingNumber = buildingNumber, let street = street {
            return "\(buildingNumber) \(street)"
        } else if buildingName != nil {
            return buildingName
        } else {
            return nil
        }
    }
}
-2
источник

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

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

Например

var tempVar: String?
print(tempVar!)

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

print(tempVar ?? "") 

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

-3
источник

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

cell

-4
источник

Посмотрите другие вопросы по меткам или Задайте вопрос