Что означает & ldquo; фатальная ошибка: неожиданно обнаружен ноль при развертывании необязательного значения & rdquo; имею в виду?

Это может сэкономить вам несколько часов на поиск.

Помните: ваш класс Child только наследует свойства DEFINED в классе Parent ... Итак, если вы создаете экземпляр объекта с использованием родительского класса, а затем заселяете он с данными, то эти данные НЕ будут доступны в вашем дочернем классе ...

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

Простое простое решение не расширяет ничего, просто передайте объект $ вашего родительского класса в ваш дочерний класс через конструктор. Таким образом, у вас есть доступ ко всем свойствам и методам объекта, сгенерированного родительским классом

Пример

class child {

    public parentObject;

    public function __construct($parentObject) {
        $this->parentObject = $parentObject;
    }

}

Если ваш $ parentObject имеет общедоступное свойство $ name, вы можете доступ к нему внутри дочернего класса с помощью функции типа:

public function print_name() {
    echo $this->parentObject->name;
}
370
задан 8 revs, 5 users 58% 19 May 2018 в 14:26
поделиться

12 ответов

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

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

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

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

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

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


Как мне безопасно обращаться с опциональными параметрами?

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

if anOptionalInt != nil {
    print("Contains a value!")
} else {
    print("Doesn’t 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("Doesn’t contain a number")
}

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

614
ответ дан 31 revs, 16 users 59% 19 May 2018 в 14:26
поделиться

TL; DR ответ

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

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

Объявить переменную необязательной (?), неявно развернутые необязательные опции (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)")

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

62
ответ дан 6 revs, 3 users 91% 19 May 2018 в 14:26
поделиться

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

для подробностей.

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

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

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

11
ответ дан QiunCheng 19 May 2018 в 14:26
поделиться

Так как приведенные выше ответы ясно объясняют, как безопасно играть с 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)

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

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

Даже я должен обернуть (не каламбур) мою голову вокруг Факультативного: D Счастливого Свертывания Все .

11
ответ дан Tharzeez 19 May 2018 в 14:26
поделиться

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

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
}

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

6
ответ дан Wissa 19 May 2018 в 14:26
поделиться

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

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

0
ответ дан Ale Mohamad 19 May 2018 в 14:26
поделиться

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

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
ответ дан midhun p 19 May 2018 в 14:26
поделиться

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

Предпосылки:

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

Существует также конструкция, называемая «оператор слияния ноль». Он принимает форму «необязательный_вар», «замена_вал». Он возвращает необязательную переменную того же типа, что и данные, содержащиеся в необязательной переменной. Если необязательный параметр содержит nil, он возвращает значение выражения после «??» символ. [1 123]

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

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

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

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

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

var foo: String!

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

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

var foo: String!


let upperFoo = foo.capitalizedString

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

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

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

39
ответ дан Duncan C 19 May 2018 в 14:26
поделиться

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

Существует несколько сценариев, приводящих к такого рода фатальной ошибке:

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

    let user = someVariable!
    

    Если someVariable равен нулю, то вы ' Я получу крушение. Делая принудительную развёртку, вы перекладывали ответственность за проверку на ноль от компилятора к вам, в основном, выполняя принудительную развёртку, вы гарантируете компилятору, что там никогда не будет нулевых значений. И угадайте, что произойдет, если каким-то образом значение 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 = "my@email.com". Или объявите выход как опциональный, хотя в этом случае компилятор заставит вас развернуть его по всему коду.

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

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

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

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

  6. [Тысяча сто двадцать семь]
5
ответ дан 4 revs 19 May 2018 в 14:26
поделиться

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

-1
ответ дан 3 November 2019 в 23:30
поделиться

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

Что-то простое, которое стоит проверить, смотрит ли все остальное хорошо

0
ответ дан 22 November 2019 в 23:47
поделиться
Другие вопросы по тегам:

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