Que signifie "Erreur fatale: néant inopinément trouvé lors du déballage d'une valeur facultative"?
Mon programme Swift plante avec l' EXC_BAD_INSTRUCTION
une des erreurs similaires suivantes. Que signifie cette erreur et comment la corriger?
Erreur fatale: nul trouvé de manière inattendue lors du déballage d'une valeur facultative
ou
Erreur fatale: trouvé nul de manière inattendue lors du déballage implicite d'une valeur facultative
Cet article est destiné à collecter des réponses aux problèmes «inopinément trouvés nil», afin qu'ils ne soient pas dispersés et difficiles à trouver. N'hésitez pas à ajouter votre propre réponse ou à modifier la réponse wiki existante.
Réponses
Cette réponse est le wiki de la communauté . Si vous pensez qu'il pourrait être amélioré, n'hésitez pas à le modifier !
Contexte: Qu'est-ce qu'une option?
Dans Swift, Optional<Wrapped>
est un type d'option : il peut contenir n'importe quelle valeur du type original ("Wrapped"), ou aucune valeur du tout (la valeur spéciale nil
). Une valeur facultative doit être déballée avant de pouvoir être utilisée.
Facultatif est un type générique , ce qui signifie que Optional<Int>
et Optional<String>
sont des types distincts - le type à l'intérieur <>
est appelé le type Wrapped. Sous le capot, une option est une énumération avec deux cas: .some(Wrapped)
et .none
, où .none
est équivalent à nil
.
Les options peuvent être déclarées en utilisant le type nommé Optional<T>
, ou (le plus souvent) comme un raccourci avec un ?
suffixe.
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
Les options sont un outil simple mais puissant pour exprimer vos hypothèses lors de l'écriture de code. Le compilateur peut utiliser ces informations pour vous éviter de faire des erreurs. À partir du langage de programmation Swift :
Swift est un langage de type sécurisé , ce qui signifie que le langage vous aide à être clair sur les types de valeurs avec lesquels votre code peut fonctionner. Si une partie de votre code nécessite une
String
, la sécurité de type vous empêche de la transmettreInt
par erreur. De même, la sécurité de type vous empêche de passer accidentellement un optionnelString
à un morceau de code qui nécessite un non-optionnelString
. La sécurité des types vous aide à détecter et à corriger les erreurs le plus tôt possible dans le processus de développement.
Certains autres langages de programmation ont également des types d'options génériques : par exemple, Peut - être dans Haskell, option dans Rust et facultatif dans C ++ 17.
Dans les langages de programmation sans types d'option, une valeur "sentinelle" particulière est souvent utilisée pour indiquer l'absence d'une valeur valide. En Objective-C, par exemple, nil
(le pointeur nul ) représente l'absence d'un objet. Pour les types primitifs tels que int
, un pointeur nul ne peut pas être utilisé, vous auriez donc besoin d'une variable distincte (telle que value: Int
et isValid: Bool
) ou d'une valeur sentinelle désignée (telle que -1
ou INT_MIN
). Ces approches sont sujettes aux erreurs car il est facile d'oublier de vérifier isValid
ou de vérifier la valeur sentinelle. De plus, si une valeur particulière est choisie comme sentinelle, cela signifie qu'elle ne peut plus être traitée comme une valeur valide .
Les types d'options tels que ceux de Swift Optional
résolvent ces problèmes en introduisant une nil
valeur spéciale et distincte (pour que vous n'ayez pas à désigner une valeur sentinelle), et en tirant parti du système de type fort pour que le compilateur puisse vous aider à vous rappeler de vérifier la valeur nulle si nécessaire.
Pourquoi ai-je reçu " erreur fatale: inopinément trouvé nul lors du déballage d'une valeur facultative "?
Pour accéder à la valeur d'une option (si elle en a une), vous devez la déballer . Une valeur facultative peut être déballée en toute sécurité ou de force. Si vous forcez le déballage d'un optionnel et qu'il n'a pas de valeur, votre programme plantera avec le message ci-dessus.
Xcode vous montrera le crash en mettant en évidence une ligne de code. Le problème se produit sur cette ligne.
Ce crash peut se produire avec deux types de dépliage forcé:
1. Déballage forcé explicite
Cela se fait avec l' !
opérateur sur une option. Par exemple:
let anOptionalString: String?
print(anOptionalString!) // <- CRASH
Erreur fatale: nul trouvé de manière inattendue lors du déballage d'une valeur facultative
Comme anOptionalString
est nil
ici, vous obtiendrez un accident sur la ligne où vous forcez le déballer.
2. Options optionnelles implicitement non emballées
Celles-ci sont définies par un !
, plutôt que par un ?
après le type.
var optionalDouble: Double! // this value is implicitly unwrapped wherever it's used
Ces options sont supposées contenir une valeur. Par conséquent, chaque fois que vous accédez à une option non emballée implicitement, elle sera automatiquement déballée de force pour vous. S'il ne contient pas de valeur, il plantera.
print(optionalDouble) // <- CRASH
Erreur fatale: trouvé nul de manière inattendue lors du déballage implicite d' une valeur facultative
Afin de déterminer quelle variable a causé le plantage, vous pouvez maintenir ⌥en cliquant pour afficher la définition, où vous pouvez trouver le type facultatif.
Les IBOutlets, en particulier, sont généralement des options implicitement déroulées. C'est parce que votre xib ou votre storyboard reliera les prises au moment de l'exécution, après l' initialisation. Vous devez donc vous assurer que vous n'accédez pas aux prises avant leur chargement. Vous devez également vérifier que les connexions sont correctes dans votre fichier storyboard / xib, sinon les valeurs seront nil
au moment de l'exécution, et donc plantent lorsqu'elles sont implicitement déballées . Lors de la fixation des connexions, essayez de supprimer les lignes de code qui définissent vos prises, puis reconnectez-les.
Quand devrais-je forcer le déballage d'une option?
Déballage de force explicite
En règle générale, vous ne devez jamais forcer explicitement le déballage d'un optionnel avec l' !
opérateur. Il peut y avoir des cas où l'utilisation !
est acceptable - mais vous ne devriez l'utiliser que si vous êtes sûr à 100% que l'option contient une valeur.
Bien qu'il puisse y avoir une occasion où vous pouvez utiliser le déballage forcé, comme vous le savez pour un fait qu'un optionnel contient une valeur - il n'y a pas un seul endroit où vous ne pouvez pas dérouler en toute sécurité cet optionnel à la place.
Options optionnelles implicitement non emballées
Ces variables sont conçues pour que vous puissiez reporter leur affectation à plus tard dans votre code. Il est de votre responsabilité de vous assurer qu'ils ont une valeur avant d'y accéder. Cependant, comme ils impliquent un déballage forcé, ils sont toujours intrinsèquement dangereux - car ils supposent que votre valeur est non-nulle, même si l'attribution de zéro est valide.
Vous ne devriez utiliser que des options implicitement non emballées en dernier recours . Si vous pouvez utiliser une variable différée , ou fournir une valeur par défaut pour une variable, vous devriez le faire au lieu d'utiliser un optionnel implicitement déroulé.
Cependant, il existe quelques scénarios où les options implicitement déballées sont bénéfiques , et vous pouvez toujours utiliser différentes manières de les déballer en toute sécurité, comme indiqué ci-dessous - mais vous devez toujours les utiliser avec prudence.
Comment puis-je traiter en toute sécurité les options?
Le moyen le plus simple de vérifier si un optionnel contient une valeur est de le comparer à nil
.
if anOptionalInt != nil {
print("Contains a value!")
} else {
print("Doesn’t contain a value.")
}
Cependant, 99,9% du temps lorsque vous travaillez avec des options, vous voudrez en fait accéder à la valeur qu'elle contient, si elle en contient une. Pour ce faire, vous pouvez utiliser la liaison facultative .
Reliure facultative
La liaison facultative vous permet de vérifier si une option contient une valeur - et vous permet d'attribuer la valeur non emballée à une nouvelle variable ou constante. Il utilise la syntaxe if let x = anOptional {...}
ou if var x = anOptional {...}
, selon si vous devez modifier la valeur de la nouvelle variable après la liaison.
Par exemple:
if let number = anOptionalInt {
print("Contains a value! It is \(number)!")
} else {
print("Doesn’t contain a number")
}
Cela fait d'abord vérifier que l'option contient une valeur. Si c'est le cas , alors la valeur «non emballée» est affectée à une nouvelle variable ( number
) - que vous pouvez ensuite utiliser librement comme si elle n'était pas facultative. Si l'option facultative ne contient pas de valeur, la clause else sera invoquée, comme vous vous en doutez.
Ce qui est bien avec la liaison facultative, c'est que vous pouvez déplier plusieurs options en même temps. Vous pouvez simplement séparer les instructions par une virgule. L'instruction réussira si toutes les options ont été déballées.
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")
}
Une autre astuce intéressante est que vous pouvez également utiliser des virgules pour vérifier une certaine condition sur la valeur, après l'avoir déballée.
if let number = anOptionalInt, number > 0 {
print("anOptionalInt contains a value: \(number), and it’s greater than zero!")
}
Le seul problème avec l'utilisation de la liaison facultative dans une instruction if, est que vous ne pouvez accéder à la valeur déballée qu'à partir de la portée de l'instruction. Si vous avez besoin d'accéder à la valeur en dehors de la portée de l'instruction, vous pouvez utiliser une instruction de garde .
Une instruction de garde vous permet de définir une condition de succès - et la portée actuelle ne continuera à s'exécuter que si cette condition est remplie. Ils sont définis avec la syntaxe guard condition else {...}
.
Donc, pour les utiliser avec une liaison facultative, vous pouvez le faire:
guard let number = anOptionalInt else {
return
}
(Notez que dans le corps de garde, vous devez utiliser l'une des instructions de transfert de contrôle afin de quitter la portée du code en cours d'exécution).
Si anOptionalInt
contient une valeur, elle sera déroulée et affectée à la nouvelle number
constante. Le code après la garde continuera alors à s'exécuter. S'il ne contient pas de valeur, le gardien exécutera le code entre crochets, ce qui entraînera un transfert de contrôle, de sorte que le code immédiatement après ne sera pas exécuté.
La vraie chose intéressante à propos des instructions de garde est que la valeur non emballée est maintenant disponible pour être utilisée dans le code qui suit l'instruction (car nous savons que le futur code ne peut s'exécuter que si l'option facultative a une valeur). C'est un excellent moyen d'éliminer les «pyramides de malheur» créées en imbriquant plusieurs instructions if.
Par exemple:
guard let number = anOptionalInt else {
return
}
print("anOptionalInt contains a value, and it’s: \(number)!")
Les gardes prennent également en charge les mêmes astuces intéressantes que l'instruction if prise en charge, telles que le déballage de plusieurs options en même temps et l'utilisation de la where
clause.
Le fait que vous utilisiez une instruction if ou guard dépend complètement de la question de savoir si un code futur nécessite que l'option facultative contienne une valeur.
Nil Opérateur de coalescence
L' opérateur de coalescence Nil est une version abrégée astucieuse de l' opérateur conditionnel ternaire , principalement conçu pour convertir les options en non-option. Il a la syntaxe a ?? b
, où a
est un type facultatif et b
est du même type que a
(bien que généralement non facultatif).
Il vous permet essentiellement de dire «Si a
contient une valeur, dépliez-la. Si ce n'est pas le cas, revenez à la b
place ». Par exemple, vous pouvez l'utiliser comme ceci:
let number = anOptionalInt ?? 0
Cela définira une number
constante de Int
type, qui contiendra soit la valeur de anOptionalInt
, si elle contient une valeur, soit 0
autre.
C'est juste un raccourci pour:
let number = anOptionalInt != nil ? anOptionalInt! : 0
Chaînage facultatif
Vous pouvez utiliser le chaînage facultatif pour appeler une méthode ou accéder à une propriété sur un fichier facultatif. Cela se fait simplement en ajoutant au nom de la variable un suffixe ?
lors de son utilisation.
Par exemple, disons que nous avons une variable foo
, de type une Foo
instance facultative .
var foo : Foo?
Si nous voulions appeler une méthode foo
qui ne retourne rien, nous pouvons simplement faire:
foo?.doSomethingInteresting()
Si foo
contient une valeur, cette méthode sera appelée dessus. Si ce n'est pas le cas, rien de grave ne se passera - le code continuera simplement à s'exécuter.
(Il s'agit d'un comportement similaire à l'envoi de messages nil
en Objective-C)
Cela peut donc également être utilisé pour définir des propriétés ainsi que des méthodes d'appel. Par exemple:
foo?.bar = Bar()
Encore une fois, rien de mauvais ne se passera ici si foo
c'est le cas nil
. Votre code continuera simplement à s'exécuter.
Une autre astuce intéressante que le chaînage facultatif vous permet de faire est de vérifier si la définition d'une propriété ou l'appel d'une méthode a réussi. Vous pouvez le faire en comparant la valeur de retour à nil
.
(C'est parce qu'une valeur optionnelle retournera Void?
plutôt que Void
sur une méthode qui ne renvoie rien)
Par exemple:
if (foo?.bar = Bar()) != nil {
print("bar was set successfully")
} else {
print("bar wasn’t set successfully")
}
Cependant, les choses deviennent un peu plus délicates lorsque vous essayez d'accéder à des propriétés ou d'appeler des méthodes qui renvoient une valeur. Parce que foo
c'est facultatif, tout ce qui en retourne sera également facultatif. Pour gérer cela, vous pouvez soit dérouler les options qui sont renvoyées en utilisant l'une des méthodes ci-dessus, soit se dérouler foo
avant d'accéder aux méthodes ou d'appeler des méthodes qui renvoient des valeurs.
De plus, comme son nom l'indique, vous pouvez «enchaîner» ces déclarations. Cela signifie que si foo
a une propriété facultative baz
, qui a une propriété qux
- vous pouvez écrire ce qui suit:
let optionalQux = foo?.baz?.qux
Encore une fois, étant donné que foo
et baz
sont facultatifs, la valeur renvoyée par qux
sera toujours une option, qu'elle soit qux
elle-même facultative ou non.
map
et flatMap
Une fonctionnalité souvent sous-utilisée avec les options est la possibilité d'utiliser les fonctions map
et flatMap
. Celles-ci vous permettent d'appliquer des transformations non facultatives à des variables facultatives. Si un optionnel a une valeur, vous pouvez lui appliquer une transformation donnée. S'il n'a pas de valeur, il restera nil
.
Par exemple, disons que vous avez une chaîne facultative:
let anOptionalString:String?
En lui appliquant la map
fonction - nous pouvons utiliser la stringByAppendingString
fonction afin de la concaténer à une autre chaîne.
Étant donné que stringByAppendingString
prend un argument de chaîne non facultatif, nous ne pouvons pas saisir directement notre chaîne facultative. Cependant, en utilisant map
, nous pouvons utiliser allow stringByAppendingString
to be used if anOptionalString
has a value.
Par exemple:
var anOptionalString:String? = "bar"
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // Optional("foobar")
Cependant, si anOptionalString
n'a pas de valeur, map
retournera nil
. Par exemple:
var anOptionalString:String?
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // nil
flatMap
fonctionne de la même manière que map
, sauf qu'il vous permet de renvoyer une autre option à partir du corps de fermeture. Cela signifie que vous pouvez entrer une option dans un processus qui nécessite une entrée non facultative, mais qui peut générer une option elle-même.
try!
Le système de gestion des erreurs de Swift peut être utilisé en toute sécurité avec Do-Try-Catch :
do {
let result = try someThrowingFunc()
} catch {
print(error)
}
Si someThrowingFunc()
génère une erreur, l'erreur sera interceptée en toute sécurité dans le catch
bloc.
La error
constante que vous voyez dans le catch
bloc n'a pas été déclarée par nous - elle est automatiquement générée par catch
.
Vous pouvez également error
vous déclarer , il a l'avantage de pouvoir le caster dans un format utile, par exemple:
do {
let result = try someThrowingFunc()
} catch let error as NSError {
print(error.debugDescription)
}
L'utilisation de try
cette méthode est la bonne façon d'essayer, d'attraper et de gérer les erreurs provenant des fonctions de lancement.
Il y a aussi try?
ce qui absorbe l'erreur:
if let result = try? someThrowingFunc() {
// cool
} else {
// handle the failure, but there's no error information available
}
Mais le système de gestion des erreurs de Swift fournit également un moyen de "forcer l'essai" avec try!
:
let result = try! someThrowingFunc()
Les concepts expliqués dans cet article s'appliquent également ici: si une erreur est levée, l'application plantera.
Vous ne devriez jamais l'utiliser que try!
si vous pouvez prouver que son résultat n'échouera jamais dans votre contexte - et c'est très rare.
La plupart du temps, vous utiliserez le système Do-Try-Catch complet - et celui en option try?
, dans les rares cas où la gestion de l'erreur n'est pas importante.
Ressources
Réponse TL; DR
À de très rares exceptions près , cette règle est d'or:
Évitez d'utiliser !
Déclarez la variable optionnelle ( ?
), non les options implicitement non déroulées (IUO) ( !
)
En d'autres termes, utilisez plutôt:
var nameOfDaughter: String?
Au lieu de:
var nameOfDaughter: String!
Développer la variable facultative à l'aide de if let
ouguard let
Soit une variable dépliée comme celle-ci:
if let nameOfDaughter = nameOfDaughter {
print("My daughters name is: \(nameOfDaughter)")
}
Ou comme ça:
guard let nameOfDaughter = nameOfDaughter else { return }
print("My daughters name is: \(nameOfDaughter)")
Cette réponse était destinée à être concise, pour une compréhension complète lire la réponse acceptée
Ressources
Cette question revient TOUT LE TEMPS sur SO. C'est l'une des premières choses avec lesquelles les nouveaux développeurs Swift ont du mal.
Contexte:
Swift utilise le concept de "Optionals" pour traiter des valeurs qui pourraient contenir une valeur, ou non. Dans d'autres langages comme C, vous pouvez stocker une valeur de 0 dans une variable pour indiquer qu'elle ne contient aucune valeur. Cependant, que faire si 0 est une valeur valide? Ensuite, vous pouvez utiliser -1. Et si -1 est une valeur valide? Etc.
Les options Swift vous permettent de configurer une variable de n'importe quel type pour qu'elle contienne soit une valeur valide, soit aucune valeur.
Vous mettez un point d'interrogation après le type lorsque vous déclarez une variable à signifier (type x, ou aucune valeur).
Un optionnel est en fait un conteneur qui contient soit une variable d'un type donné, soit rien.
Un optionnel doit être "déballé" afin de récupérer la valeur à l'intérieur.
Le "!" L'opérateur est un opérateur de "déroulement forcé". Il dit "faites-moi confiance. Je sais ce que je fais. Je vous garantis que lorsque ce code s'exécutera, la variable ne contiendra pas nil." Si vous vous trompez, vous vous écrasez.
À moins que vous ne sachiez vraiment ce que vous faites, évitez le "!" forcer l'opérateur à déballer. C'est probablement la plus grande source de plantages pour les programmeurs Swift débutants.
Comment gérer les options:
Il existe de nombreuses autres façons de traiter les options qui sont plus sûres. En voici quelques-uns (liste non exhaustive)
Vous pouvez utiliser "liaison facultative" ou "if let" pour dire "si cette option contient une valeur, enregistrez cette valeur dans une nouvelle variable non facultative. Si l'option facultative ne contient pas de valeur, ignorez le corps de cette instruction if ".
Voici un exemple de liaison facultative avec notre foo
option:
if let newFoo = foo //If let is called optional binding. {
print("foo is not nil")
} else {
print("foo is nil")
}
Notez que la variable que vous définissez lorsque vous utilisez l'option de soumission optionnelle n'existe que (est uniquement "dans la portée") dans le corps de l'instruction if.
Alternativement, vous pouvez utiliser une instruction de garde, qui vous permet de quitter votre fonction si la variable est nulle:
func aFunc(foo: Int?) {
guard let newFoo = input else { return }
//For the rest of the function newFoo is a non-optional var
}
Des instructions Guard ont été ajoutées dans Swift 2. Guard vous permet de conserver le "chemin d'or" à travers votre code, et d'éviter les niveaux toujours croissants de if imbriqués qui résultent parfois de l'utilisation de la liaison optionnelle "if let".
Il existe également une construction appelée "opérateur de fusion nil". Il prend la forme "facultatif_var ?? replacement_val". Il renvoie une variable non facultative du même type que les données contenues dans l'option. Si l'option contient nil, elle renvoie la valeur de l'expression après le "??" symbole.
Vous pouvez donc utiliser un code comme celui-ci:
let newFoo = foo ?? "nil" // "??" is the nil coalescing operator
print("foo = \(newFoo)")
Vous pouvez également utiliser la gestion des erreurs try / catch ou guard, mais généralement l'une des autres techniques ci-dessus est plus propre.
ÉDITER:
Un autre piège un peu plus subtil avec les options est "les options implicitement non emballées. Lorsque nous déclarons foo, nous pourrions dire:
var foo: String!
Dans ce cas, foo est toujours facultatif, mais vous n'avez pas à le dérouler pour le référencer. Cela signifie que chaque fois que vous essayez de référencer foo, vous plantez si c'est nul.
Donc ce code:
var foo: String!
let upperFoo = foo.capitalizedString
Va planter sur la référence à la propriété capitalizedString de foo même si nous ne forçons pas le déballage de foo. l'impression a l'air bien, mais ce n'est pas le cas.
Ainsi, vous voulez être très prudent avec les options implicitement non emballées. (et peut-être même les éviter complètement jusqu'à ce que vous ayez une solide compréhension des options.)
Bottom line: Lorsque vous apprenez Swift pour la première fois, faites comme si le "!" le caractère ne fait pas partie de la langue. Cela risque de vous causer des ennuis.
Puisque les réponses ci-dessus expliquent clairement comment jouer en toute sécurité avec les options. Je vais essayer d'expliquer quelles options sont vraiment rapides.
Une autre façon de déclarer une variable facultative est
var i : Optional<Int>
Et le type facultatif n'est rien d'autre qu'une énumération avec deux cas, c'est-à-dire
enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
.
.
.
}
Donc, pour affecter un nul à notre variable 'i'. On peut faire
var i = Optional<Int>.none
ou assigner une valeur, on va passer une certaine valeur
var i = Optional<Int>.some(28)
Selon Swift, «nul» est l'absence de valeur. Et pour créer une instance initialisée avec nil
Nous devons nous conformer à un protocole appelé ExpressibleByNilLiteral
et excellent si vous l'avez deviné, il est déconseillé de se Optionals
conformer ExpressibleByNilLiteral
et de se conformer aux autres types.
ExpressibleByNilLiteral
a une seule méthode appelée init(nilLiteral:)
qui initialise un instace avec nil. Vous n'appelerez généralement pas cette méthode et selon la documentation rapide, il est déconseillé d'appeler cet initialiseur directement car le compilateur l'appelle chaque fois que vous initialisez un type facultatif avec un nil
littéral.
Même moi-même doit envelopper (sans jeu de mots) ma tête autour d' options : D Happy Swfting All .
Tout d'abord, vous devez savoir ce qu'est une valeur facultative. Vous pouvez accéder au langage de programmation Swift pour plus de détails.
Deuxièmement, vous devez savoir que la valeur facultative a deux statuts. L'un est la valeur complète et l'autre est une valeur nulle. Donc, avant d'implémenter une valeur facultative, vous devez vérifier de quel état il s'agit.
Vous pouvez utiliser if let ...
ou guard let ... else
et ainsi de suite.
Une autre façon, si vous ne souhaitez pas vérifier l'état de la variable avant votre implémentation, vous pouvez également utiliser à la var buildingName = buildingName ?? "buildingName"
place.
J'ai eu cette erreur une fois lorsque j'essayais de définir les valeurs de mes points de vente à partir de la méthode prepare for segue comme suit:
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
}
}
}
Ensuite, j'ai découvert que je ne pouvais pas définir les valeurs des sorties du contrôleur de destination car le contrôleur n'avait pas encore été chargé ou initialisé.
Alors je l'ai résolu de cette façon:
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)
}
}
}
Contrôleur de destination:
// 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
}
J'espère que cette réponse aidera quiconque a le même problème car j'ai trouvé que la réponse marquée est une excellente ressource pour la compréhension des options et de leur fonctionnement, mais n'a pas abordé le problème lui-même directement.
En gros, vous avez essayé d'utiliser une valeur nulle dans les endroits où Swift n'autorise que des valeurs non nulles, en disant au compilateur de vous faire confiance qu'il n'y aura jamais de valeur nulle, permettant ainsi à votre application de se compiler.
Il existe plusieurs scénarios qui conduisent à ce type d'erreur fatale:
déballage forcé:
let user = someVariable!
Si
someVariable
c'est nul, alors vous aurez un crash. En effectuant un déballage forcé, vous avez déplacé la responsabilité de vérification nil du compilateur vers vous, essentiellement en effectuant un déballage forcé, vous garantissez au compilateur que vous n'aurez jamais de valeurs nulles. Et devinez ce qu'il se passe si d'une manière ou d'une autre une valeur nulle se terminesomeVariable
?Solution? Utilisez une liaison facultative (aka if-let), faites le traitement des variables ici:
if user = someVariable { // do your stuff }
lancers forcés (vers le bas):
let myRectangle = someShape as! Rectangle
Ici, par la conversion forcée, vous dites au compilateur de ne plus s'inquiéter, car vous y trouverez toujours une
Rectangle
instance. Et tant que cela tient, vous n'avez pas à vous inquiéter. Les problèmes commencent lorsque vous ou vos collègues du projet commencez à faire circuler des valeurs non rectangulaires.Solution? Utilisez une liaison facultative (aka if-let), faites le traitement des variables ici:
if let myRectangle = someShape as? Rectangle { // yay, I have a rectangle }
Options optionnelles implicitement déroulées. Supposons que vous ayez la définition de classe suivante:
class User { var name: String! init() { name = "(unnamed)" } func nicerName() { return "Mr/Ms " + name } }
Maintenant, si personne ne gâche la
name
propriété en la définissant surnil
, cela fonctionne comme prévu, mais siUser
est initialisé à partir d'un JSON qui n'a pas laname
clé, vous obtenez l'erreur fatale lorsque vous essayez d'utiliser la propriété.Solution? Ne les utilisez pas :) Sauf si vous êtes sûr à 102% que la propriété aura toujours une valeur non nulle au moment où elle doit être utilisée. Dans la plupart des cas, la conversion en option ou non fonctionnera. En le rendant non facultatif, le compilateur vous aidera également en indiquant les chemins de code que vous avez manqués en donnant une valeur à cette propriété
Prises non connectées ou pas encore connectées. C'est un cas particulier du scénario n ° 3. En gros, vous avez une classe chargée XIB que vous souhaitez utiliser.
class SignInViewController: UIViewController { @IBOutlet var emailTextField: UITextField! }
Maintenant, si vous avez manqué de connecter la prise à partir de l'éditeur XIB, l'application se bloquera dès que vous voudrez utiliser la prise. Solution? Assurez-vous que toutes les prises sont connectées. Ou utilisez l'
?
opérateur sur euxemailTextField?.text = "[email protected]"
. Ou déclarez la sortie comme facultative, bien que dans ce cas, le compilateur vous obligera à la déballer dans tout le code.Valeurs provenant d'Objective-C, et qui n'ont pas d'annotations de nullabilité. Supposons que nous ayons la classe Objective-C suivante:
@interface MyUser: NSObject @property NSString *name; @end
Désormais, si aucune annotation de nullabilité n'est spécifiée (soit explicitement, soit via
NS_ASSUME_NONNULL_BEGIN
/NS_ASSUME_NONNULL_END
), laname
propriété sera importée dans Swift en tant queString!
(un IUO - implicitement déroulé en option). Dès qu'un code swift voudra utiliser la valeur, il plantera si ellename
est nulle.Solution? Ajoutez des annotations de nullité à votre code Objective-C. Attention cependant, le compilateur Objective-C est un peu permissif en ce qui concerne la nullité, vous pouvez vous retrouver avec des valeurs nulles, même si vous les avez explicitement marquées comme
nonnull
.
C'est plus un commentaire important et c'est pourquoi les options implicitement non emballées peuvent être trompeuses lorsqu'il s'agit de déboguer les nil
valeurs.
Pensez au code suivant: Il se compile sans erreurs / avertissements:
c1.address.city = c3.address.city
Pourtant, au moment de l'exécution, il donne l'erreur suivante: Erreur fatale: trouvé nul de manière inattendue lors du déballage d'une valeur facultative
Pouvez-vous me dire quel est l'objet nil
?
Vous ne pouvez pas!
Le code complet serait:
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
}
En bref, en utilisant var address : Address!
vous cachez la possibilité qu'une variable puisse provenir nil
d'autres lecteurs. Et quand il plante, vous vous dites "que diable?! Mon address
n'est pas facultatif, alors pourquoi est-ce que je plante?!.
Il est donc préférable d'écrire comme tel:
c1.address.city = c2.address!.city // ERROR: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Pouvez-vous maintenant me dire de quel objet il s'agissait nil
?
Cette fois, le code vous a été rendu plus clair. Vous pouvez rationaliser et penser que c'est probablement le address
paramètre qui a été déballé avec force.
Le code complet serait:
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
}
Les erreurs EXC_BAD_INSTRUCTION
et fatal error: unexpectedly found nil while implicitly unwrapping an Optional value
apparaissent le plus lorsque vous avez déclaré un @IBOutlet
, mais pas connecté au storyboard .
Vous devriez également en savoir plus sur le fonctionnement des options , mentionné dans d'autres réponses, mais c'est le seul moment qui me semble le plus souvent.
Si vous obtenez cette erreur dans CollectionView, essayez également de créer un fichier CustomCell et un fichier xib personnalisé.
ajoutez ce code dans ViewDidLoad () à mainVC.
let nib = UINib(nibName: "CustomnibName", bundle: nil)
self.collectionView.register(nib, forCellWithReuseIdentifier: "cell")
Je suis tombé sur cette erreur en effectuant une transition d'un contrôleur de vue de table vers un contrôleur de vue car j'avais oublié de spécifier le nom de classe personnalisé pour le contrôleur de vue dans le storyboard principal.
Quelque chose de simple qui vaut la peine de vérifier si tout le reste va bien