Страницы

Поиск по вопросам

среда, 29 января 2020 г.

Объяснение по fatal error: unexpectedly found nil while unwrapping an Optional value

#swift


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


Ответы

Ответ 1



Давайте один последний раз пройдемся по unexpectedly found nil while unwrapping an Optional value и больше никогда к нему не будем возвращаться. Когда вы создаете переменные инстанса, swift от вас хочет, чтобы вы либо сразу им присвоили значения по дефолту, либо инициализировали их в init. Во многих случаях это нам не подходит, тогда мы объявляем свои переменные как optional. var test:Int? // вопрос в конце, это объявление опциональной переменной Что значит Optional? Это значит, что вместо просто переменной, например Int, мы получаем инумерацию из двух значений: none и some(Wrapped). Вот это Wrapped и есть значение нашей переменной. Соответственно, если переменная не присвоена, то при попытке к ней обратиться, получим none т.е. nil. Если же она содержит значение мы получим Optional(value) Вот пример: var test:Int? print(test) // напечатает 'nil' test = 42 print(test) // напечатает 'Optional(42)' Далее, чтобы избавиться от этого Optional нам надо ее развернуть (unwrap), то есть сказать, что мы точно уверены, что там что то есть и мы хотим получить это самое что то. Другими словами, вместо Optional(value), получить value. Чтобы сделать принудительное разворачивание (forced unwrap) мы добавляем '!'. При этом, если переменная не присвоена, мы получим крэш - тот самый unexpectedly found nil while unwrapping an Optional value. Пример: var test:Int? print(test!) // крэш unexpectedly found nil и т.д. var test2:Int? test2 = 42 print(test2!) // напечатает 42 Дальше, если мы хотим создать переменную как Optional, но точно знаем, что в нее будет что-то присвоено до того, как мы ее будем читать первый раз, мы можем ее объявить как Implicitly Unwrapped Optional Type, посредством добавления '!' в объявлении. В этом случае, она опять же будет Optional, но система будет ее принудительно разворачивать (force unwrap) при любом обращении к ней. var test:Int! // '!' в конце, это обявление implicitly unwrapped optional print(test) // крэш unexpectedly found nil... var test2:Int! test2 = 42 print(test2) // напечатает 42 А что делать, если мы понятия не имеем есть там что то в этой optional или нет? var test:Int? if(test != nil) { print(test!) // развернутое значение } else { print(test) // nil } Либо другой способ, который создает временную переменную с развернутым значением var test:Int? if let unwrappedTest = test { print(unwrappedTest) } else { print("no value") } UPDATE: то же самое через guard var test:Int? guard let unwrappedTest = test else { print("No value") return // либо throw по ситуации } print(unwrappedTest) Ну и если вам надо force unwrapped превратить обратно в Optional: var test:Int! print(test as Optional) // напечатает nil Optional chaining: Есть еще такая ситуация, когда надо сделать цепочку вызовов, при этом где то в цепочке может содержаться опциональная перменная. Вот такой пример дает apple: Допустим есть класс Residence, и в нем есть переменная numberOfRooms; и есть класс Person, который имеет опциональную переменную типа Residence. Другими словами у человека может быть жилье, с каким то количеством комнат, а может и не быть жилья. То есть при попытке обратиться к человек-жилье-комнаты, мы можем наткнуться на nil вместо жилья. class Person { var residence: Residence? } class Residence { var numberOfRooms = 1 } Теперь мы хотим создать новый Person, и узнать сколько комнат в его жилище (при этом residence может быть nil поскольку он Optional). Для этого мы говорим системе, что мы хотим делать с опциональным значением - либо оставлять его опциональным ('?') со всеми вытекающими последствиями, в частности получить nil вместо количества комнат; либо принудительно его разворачивать, при этом имея риск получить fatal error. let john = Person() print(john.residence!.numberOfRooms) // крэш unexpectedly ... print(john.residence?.numberOfRooms) // напечатает nil let tom = Person() tom.residence = Residence() print(tom.residence!.numberOfRooms) // напечатает 1 print(tom.residence?.numberOfRooms) // напечатает Optional(1) Тут надо заметить, что хотя numberOfRooms не является опциональной, она в этой цепи становится таковой, поскольку цепь идет через опциональную residence?

Комментариев нет:

Отправить комментарий