Mój program Swift ulega awarii EXC_BAD_INSTRUCTION
i występuje jeden z następujących podobnych błędów. Co oznacza ten błąd i jak go naprawić?
Błąd krytyczny: nieoczekiwanie znaleziono zero podczas rozpakowywania wartości opcjonalnej
lub
Błąd krytyczny: nieoczekiwanie znaleziono zero podczas niejawnego rozpakowywania wartości opcjonalnej
Ten post ma na celu zebranie odpowiedzi na problemy „nieoczekiwanie znalezione zero”, aby nie były rozproszone i trudne do znalezienia. Dodaj własną odpowiedź lub edytuj istniejącą odpowiedź wiki.
swift
exception
error-handling
pkamb
źródło
źródło
Odpowiedzi:
Ta odpowiedź to wiki społeczności . Jeśli uważasz, że można to poprawić, możesz go edytować !
Tło: co jest opcjonalne?
W Swift
Optional
jest typem ogólnym, który może zawierać wartość (dowolnego rodzaju) lub nie zawierać żadnej wartości.W wielu innych językach programowania często stosuje się określoną wartość „wartownika”, aby wskazać brak wartości . Na przykład w Objective-C
nil
( wskaźnik zerowy ) wskazuje na brak obiektu. Ale staje się to trudniejsze podczas pracy z typami pierwotnymi - czy powinno się-1
go używać do wskazywania braku liczby całkowitej, a możeINT_MIN
innej liczby całkowitej? Jeśli jakakolwiek konkretna wartość zostanie wybrana jako „brak liczby całkowitej”, oznacza to, że nie można jej już traktować jako wartości prawidłowej .Swift jest językiem bezpiecznym dla typu, co oznacza, że język ten pomaga w zrozumieniu typów wartości, z którymi może współpracować kod. Jeśli część kodu oczekuje ciągu, bezpieczeństwo typu zapobiega przypadkowemu przekazaniu go jako ciągu.
W Swift każdy typ może być opcjonalny . Opcjonalna wartość może przyjmować dowolną wartość z typu oryginalnego lub wartość specjalną
nil
.Opcje są definiowane za pomocą
?
przyrostka na typ:Brak wartości opcjonalnej jest wskazywany przez
nil
:(Zauważ, że to
nil
nie jest to samo, conil
w Objective-C. W Objective-Cnil
oznacza brak prawidłowego wskaźnika obiektu ; w Swift, Optionals nie są ograniczone do obiektów / typów referencji. Opcjonalne zachowuje się podobnie do Może Haskella .)Dlaczego dostałem „ błąd krytyczny: nieoczekiwanie znaleziono zero podczas rozpakowywania wartości opcjonalnej ”?
Aby uzyskać dostęp do wartości opcjonalnej (jeśli w ogóle ją posiada), musisz ją rozpakować . Opcjonalną wartość można bezpiecznie rozpakować lub wymusić. Jeśli wymusisz rozpakowanie opcjonalnego, a nie miał on wartości, twój program zawiesi się z powyższym komunikatem.
Xcode pokaże awarię, podświetlając wiersz kodu. Problem występuje na tej linii.
Ta awaria może wystąpić z dwoma różnymi rodzajami rozpakowywania wymuszonego:
1. Rozpakowywanie z jawną siłą
Odbywa się to z
!
operatorem na opcjonalnym. Na przykład:Jak
anOptionalString
jestnil
tutaj, dostaniesz awarii na linii, gdzie można wymusić unwrap niego.2. Niejawnie nieopakowane opcje
Są one zdefiniowane za pomocą
!
, a nie?
po typie.Zakłada się, że te opcje zawierają wartość. Dlatego za każdym razem, gdy uzyskujesz dostęp do domyślnie nieopakowanego opcjonalnego, zostanie ono automatycznie wymuszone. Jeśli nie zawiera wartości, nastąpi awaria.
Aby ustalić, która zmienna spowodowała awarię, możesz przytrzymać ⌥, klikając, aby wyświetlić definicję, w której możesz znaleźć typ opcjonalny.
W szczególności IBOutlety są zwykle niejawnie nieopakowanymi opcjami. Wynika to z faktu, że twój Xib lub Storyboard połączy się z gniazdami w czasie wykonywania, po inicjalizacji. Dlatego należy upewnić się, że nie uzyskuje się dostępu do gniazd przed ich załadowaniem. Należy również sprawdzić, czy połączenia są prawidłowe w pliku scenorysowym / pliku Xib, w przeciwnym razie wartości będą
nil
w czasie wykonywania, a zatem ulegną awarii, gdy zostaną niejawnie rozpakowane . Podczas ustalania połączeń spróbuj usunąć wiersze kodu definiujące gniazda, a następnie podłącz je ponownie.Kiedy powinienem wymusić rozpakowanie Opcjonalnego?
Rozpakowywanie z jawną siłą
Zasadniczo nigdy nie należy jawnie wymuszać rozpakowywania opcjonalnego z
!
operatorem. Mogą wystąpić przypadki użycia!
jest dopuszczalne - ale powinieneś go zawsze używać, jeśli masz 100% pewności, że opcja zawiera wartość.Chociaż może się zdarzyć, że możesz użyć wymuszania rozpakowywania, ponieważ wiadomo , że opcja zawiera wartość - nie ma jednego miejsca, w którym nie można bezpiecznie rozpakować tej opcji.
Opcjonalnie nieopakowane opcje
Zmienne te zostały zaprojektowane tak, aby można było odłożyć ich przypisanie na później w kodzie. To jest twój obowiązek upewnić się, że posiadają wartość, zanim do nich dostęp. Ponieważ jednak wiążą się z rozpakowywaniem siły, nadal są z natury niebezpieczne - jak zakładają twoja wartość jest różna od zera, mimo że przypisanie wartości zero jest prawidłowe.
Powinieneś używać domyślnie nieopakowanych opcji jako ostateczności . Jeśli możesz użyć leniwej zmiennej lub podać wartość domyślną dla zmiennej - powinieneś to zrobić zamiast używać opcjonalnie nieopakowanego opcjonalnego.
Istnieje jednak kilka scenariuszy, w których niejawnie nieopakowane opcje są korzystne i nadal możesz korzystać z różnych sposobów bezpiecznego ich rozpakowywania, jak podano poniżej - ale zawsze powinieneś używać ich z należytą ostrożnością.
Jak mogę bezpiecznie radzić sobie z Opcjonalnymi?
Najprostszym sposobem sprawdzenia, czy opcja zawiera wartość, jest jej porównanie
nil
.Jednak w 99,9% przypadków podczas pracy z opcjami opcjonalnymi będziesz chciał uzyskać dostęp do wartości, którą zawiera, jeśli w ogóle ją zawiera. Aby to zrobić, możesz użyć opcjonalnego wiązania .
Opcjonalne wiązanie
Opcjonalne wiązanie pozwala sprawdzić, czy opcjonalne zawiera wartość - i umożliwia przypisanie nieopakowanej wartości do nowej zmiennej lub stałej. Korzysta ze składni
if let x = anOptional {...}
lubif var x = anOptional {...}
, w zależności od tego, czy trzeba zmodyfikować wartość nowej zmiennej po jej powiązaniu.Na przykład:
Powoduje to najpierw sprawdzenie, czy opcja zawiera wartość. Jeśli to robi , to „rozpakować” wartość jest przypisana do nowej zmiennej (
number
) - który można następnie dowolnie wykorzystać tak, jakby były non-opcjonalne. Jeśli opcja nie zawiera wartości, zostanie wywołana klauzula else, jak można się spodziewać.Zaletą opcjonalnego wiązania jest to, że możesz rozpakować wiele opcji jednocześnie. Możesz po prostu oddzielić instrukcje przecinkiem. Instrukcja powiedzie się, jeśli wszystkie opcje będą rozpakowane.
Kolejną ciekawą sztuczką jest to, że po rozpakowaniu można również użyć przecinków, aby sprawdzić określony warunek wartości.
Jedynym błędem przy użyciu opcjonalnego wiązania w instrukcji if jest to, że można uzyskać dostęp do nieopakowanej wartości tylko z zakresu instrukcji. Jeśli potrzebujesz dostępu do wartości spoza zakresu instrukcji, możesz użyć instrukcji wartownika .
Oświadczenie osłona pozwala zdefiniować warunek sukcesu - a obecny zakres będzie tylko kontynuować wykonywanie jeśli warunek jest spełniony. Są one zdefiniowane za pomocą składni
guard condition else {...}
.Aby użyć ich z opcjonalnym wiązaniem, możesz to zrobić:
(Należy zauważyć, że w ciele strażnika należy użyć jednej z instrukcji transferu sterowania , aby wyjść z zakresu aktualnie wykonywanego kodu).
Jeśli
anOptionalInt
zawiera wartość, zostanie ona rozpakowana i przypisana do nowejnumber
stałej. Kod po strażniku będzie kontynuował wykonywanie. Jeśli nie zawiera wartości - wartownik wykona kod w nawiasach, co doprowadzi do przekazania kontroli, dzięki czemu kod natychmiast po nim nie zostanie wykonany.Naprawdę fajną rzeczą w instrukcjach wartowniczych jest to, że nieopakowana wartość jest teraz dostępna do użycia w kodzie następującym po instrukcji (ponieważ wiemy, że przyszły kod może zostać wykonany tylko wtedy, gdy opcja ma wartość). Jest to doskonały sposób na wyeliminowanie „piramid zagłady” utworzonych przez zagnieżdżenie wielu instrukcji if.
Na przykład:
Strażnicy obsługują również te same zgrabne sztuczki, które obsługuje instrukcja if, takie jak rozpakowywanie wielu opcji jednocześnie i korzystanie z
where
klauzuli.To, czy użyjesz instrukcji if czy guard, zależy całkowicie od tego, czy jakikolwiek przyszły kod wymaga opcjonalnego zawarcia wartości.
Brak koalescencji operatora
Nil zlewający Operator jest fajną wersję skróconą z potrójnego operatora warunkowego , przeznaczony przede wszystkim do konwersji ewentualne, aby non-optionals. Ma składnię
a ?? b
, gdziea
jest typem opcjonalnym ib
jest tego samego typu coa
(chociaż zwykle nie jest opcjonalny).Zasadniczo pozwala powiedzieć „Jeśli
a
zawiera wartość, rozpakuj ją. Jeśli nie, to wróćb
”. Na przykład możesz użyć tego w następujący sposób:Spowoduje to zdefiniowanie
number
stałejInt
typu, która będzie zawierać wartośćanOptionalInt
, jeśli zawiera wartość, lub w0
inny sposób.To tylko skrót dla:
Opcjonalne łączenie
Możesz użyć opcjonalnego łączenia w celu wywołania metody lub uzyskania dostępu do właściwości na opcjonalnym. Dokonuje się tego po prostu przez dodanie nazwy zmiennej za
?
pomocą a.Załóżmy na przykład, że mamy zmienną
foo
typu opcjonalnegoFoo
wystąpienia.Jeśli chcemy wywołać metodę
foo
, która niczego nie zwraca, możemy po prostu zrobić:Jeśli
foo
zawiera wartość, zostanie na niej wywołana ta metoda. Jeśli tak się nie stanie, nic złego się nie stanie - kod będzie po prostu kontynuował wykonywanie.(Jest to podobne zachowanie jak wysyłanie wiadomości do
nil
celu C)Można to zatem wykorzystać również do ustawiania właściwości, a także metod wywoływania. Na przykład:
Znów nic złego się tu nie stanie, jeśli
foo
jestnil
. Twój kod będzie po prostu kontynuował wykonywanie.Inną ciekawą sztuczką, którą umożliwia opcjonalne łączenie łańcuchów, jest sprawdzenie, czy ustawienie właściwości lub wywołanie metody zakończyło się powodzeniem. Możesz to zrobić, porównując wartość zwracaną z
nil
.(Jest tak, ponieważ zwracana jest wartość opcjonalna
Void?
zamiastVoid
metody, która niczego nie zwraca)Na przykład:
Sprawy stają się jednak nieco trudniejsze przy próbie uzyskania dostępu do właściwości lub wywołania metod zwracających wartość. Ponieważ
foo
jest to opcjonalne, wszystko, co zostanie z niego zwrócone, będzie również opcjonalne. Aby sobie z tym poradzić, możesz rozpakować opcje, które są zwracane za pomocą jednej z powyższych metod - lub rozpakowaćfoo
się przed uzyskaniem dostępu do metod lub wywołaniem metod, które zwracają wartości.Ponadto, jak sugeruje nazwa, można „połączyć” te instrukcje razem. Oznacza to, że jeśli
foo
ma opcjonalną właściwośćbaz
, która ma właściwośćqux
- możesz napisać następujące:Ponownie, ponieważ
foo
ibaz
są opcjonalne, wartość zwracana zqux
zawsze będzie opcjonalna niezależnie od tegoqux
, czy sama jest opcjonalna.map
iflatMap
Często niewykorzystywaną funkcją w opcjach jest możliwość korzystania z funkcji
map
iflatMap
. Pozwalają one zastosować nie opcjonalne przekształcenia do zmiennych opcjonalnych. Jeśli opcja ma wartość, możesz zastosować do niej daną transformację. Jeśli nie ma wartości, pozostanienil
.Załóżmy na przykład, że masz opcjonalny ciąg:
Stosując
map
do niej funkcję - możemy użyćstringByAppendingString
funkcji, aby połączyć ją z innym ciągiem.Ponieważ
stringByAppendingString
pobiera nie opcjonalny ciąg znaków, nie możemy bezpośrednio wprowadzić naszego opcjonalnego ciągu. Korzystając z niejmap
, możemy jednak użyć parametru allow,stringByAppendingString
jeślianOptionalString
ma wartość.Na przykład:
Jednak jeśli
anOptionalString
nie ma wartości,map
zwrócinil
. Na przykład:flatMap
działa podobniemap
, ale umożliwia zwrócenie kolejnej opcjonalnej z wnętrza korpusu zamknięcia. Oznacza to, że możesz wprowadzić opcjonalny element w procesie, który wymaga nie opcjonalnego wejścia, ale sam może wypisać opcjonalny.try!
System obsługi błędów Swift może być bezpiecznie używany z Do-Try-Catch :
Jeśli
someThrowingFunc()
zgłosi błąd, błąd zostanie bezpiecznie złapany wcatch
bloku.error
Stały widać wcatch
bloku nie zostało uznane przez nas - to automatycznie generowane przezcatch
.Możesz również zadeklarować
error
siebie, ma tę zaletę, że można go rzutować na przydatny format, na przykład:Korzystanie z
try
tej metody jest właściwym sposobem próbowania, wychwytywania i obsługi błędów pochodzących z funkcji rzucania.Istnieje również,
try?
który pochłania błąd:Ale system obsługi błędów Swift zapewnia również „wymuszenie próby” za pomocą
try!
:Obowiązują tu również pojęcia wyjaśnione w tym poście: jeśli zostanie zgłoszony błąd, aplikacja ulegnie awarii.
Powinieneś używać tylko
try!
wtedy, gdy możesz udowodnić, że jego wynik nigdy nie zawiedzie w twoim kontekście - a to bardzo rzadko.Przez większość czasu będziesz korzystać z pełnego systemu Do-Try-Catch - i opcjonalnego
try?
, w rzadkich przypadkach, w których obsługa błędu nie jest ważna.Zasoby
źródło
compactMap()
zamiastflatMap()
.Odpowiedź TL; DR
Z nielicznymi wyjątkami ta zasada jest złota:
Unikaj używania
!
Deklaracja zmiennej opcjonalnej (
?
), niejawnie nieopakowanej opcji (IUO) (!
)Innymi słowy, użyj raczej:
var nameOfDaughter: String?
Zamiast:
var nameOfDaughter: String!
Rozpakuj opcjonalną zmienną za pomocą
if let
lubguard let
Albo rozpakuj zmienną jak ta:
Lub tak:
Ta odpowiedź miała być zwięzła, dla pełnego zrozumienia przeczytaj zaakceptowaną odpowiedź
Zasoby
źródło
To pytanie pojawia się CAŁY CZAS na SO. Jest to jedna z pierwszych rzeczy, z którymi zmagają się nowi programiści Swift.
Tło:
Swift używa pojęcia „Opcjonalne” do radzenia sobie z wartościami, które mogą zawierać wartość lub nie. W innych językach, takich jak C, możesz przechowywać wartość 0 w zmiennej, aby wskazać, że nie zawiera ona żadnej wartości. Co jednak, jeśli 0 jest prawidłową wartością? Wtedy możesz użyć -1. Co jeśli -1 jest prawidłową wartością? I tak dalej.
Opcjonalne opcje szybkie pozwalają skonfigurować zmienną dowolnego typu, tak aby zawierała albo prawidłową wartość, albo żadnej wartości.
Umieszczasz znak zapytania za typem, kiedy deklarujesz zmienną jako średnią (wpisz x lub brak wartości).
Opcjonalny to tak naprawdę kontener, który zawiera albo zmienną danego typu, albo nic.
Opcjonalne musi być „rozpakowane”, aby pobrać wartość w środku.
„!” operator jest operatorem „wymuszającego rozpakowywanie”. Mówi: „zaufaj mi. Wiem, co robię. Gwarantuję, że kiedy ten kod się uruchomi, zmienna nie będzie zawierać zero”. Jeśli się mylisz, upaść.
Chyba, że naprawdę nie wiem, co robisz, uniknąć „!” wymuś operator odwijania. Jest to prawdopodobnie największe źródło awarii dla początkujących programistów Swift.
Jak radzić sobie z opcjami:
Istnieje wiele innych sposobów radzenia sobie z opcjami, które są bezpieczniejsze. Oto niektóre (nie wyczerpująca lista)
Możesz użyć „opcjonalnego wiązania” lub „jeśli pozwala”, aby powiedzieć „jeśli ta opcjonalna zawiera wartość, zapisz tę wartość w nowej, nie opcjonalnej zmiennej. Jeśli opcjonalna nie zawiera wartości, pomiń treść instrukcji if „.
Oto przykład opcjonalnego wiązania z naszym
foo
opcjonalnym:Zauważ, że zmienna, którą definiujesz, kiedy używasz opcjonalnej oferty, istnieje tylko (jest tylko „w zakresie”) w treści instrukcji if.
Alternatywnie możesz użyć instrukcji guard, która pozwala wyjść z funkcji, jeśli zmienna ma wartość zero:
Instrukcje Swift zostały dodane w Swift 2. Guard pozwala zachować „złotą ścieżkę” w kodzie i uniknąć ciągle rosnących poziomów zagnieżdżonych, jeśli czasami wynikają z użycia opcjonalnego wiązania „jeśli pozwól”.
Istnieje również konstrukcja zwana „zerowym operatorem koalescencyjnym”. Przybiera postać „opcjonalny_war ?? wartość_wymienności”. Zwraca nie opcjonalną zmienną tego samego typu, co dane zawarte w opcjonalnej. Jeśli opcjonalny zawiera zero, zwraca wartość wyrażenia po „??” symbol.
Możesz więc użyć takiego kodu:
Możesz także użyć obsługi błędów try / catch lub guard, ale ogólnie jedna z powyższych technik jest czystsza.
EDYTOWAĆ:
Inną, nieco bardziej subtelną gotcha z opcjami jest „domyślnie nieopakowane opcje. Kiedy deklarujemy foo, możemy powiedzieć:
W takim przypadku foo nadal jest opcjonalne, ale nie trzeba go rozpakowywać, aby się do niego odwoływać. Oznacza to, że za każdym razem, gdy spróbujesz odwołać się do foo, nastąpi awaria, jeśli jest ona zerowa.
Więc ten kod:
Zawiesza się w związku z właściwością capitalizedString foo, nawet jeśli nie jesteśmy foo rozpakowywaniem wymuszonym. wydruk wygląda dobrze, ale tak nie jest.
Dlatego chcesz być naprawdę ostrożny z niejawnie rozpakowanymi opcjami. (a może nawet całkowicie ich unikaj, dopóki nie zrozumiesz dobrze opcji).
Konkluzja: Kiedy uczysz się Swift po raz pierwszy, udawaj „!” znak nie jest częścią języka. To może wpędzić cię w kłopoty.
źródło
guard
klauzulach nie ma nic . Nie ma nic w użyciu,if var
którego konstrukcja jest tak samo ważna jak konstrukcjaif let
. Wwhere
klauzulach nie ma nic wartego wspomnienia, gdy mówimy oif let
wiązaniu (w wielu przypadkach usuwa całą warstwę zagnieżdżania). W opcjonalnym łańcuchowaniu nie ma nic.Ponieważ powyższe odpowiedzi jasno wyjaśniają, jak bezpiecznie grać z Opcjonalnymi. Spróbuję wyjaśnić, jakie Opcjonalnie są naprawdę szybkie.
Innym sposobem zadeklarowania zmiennej opcjonalnej jest
var i : Optional<Int>
A typ opcjonalny to nic innego jak wyliczenie z dwoma przypadkami, tj
Aby więc przypisać zero do naszej zmiennej „i”. Możemy zrobić
var i = Optional<Int>.none
lub przypisać wartość, przekażemy pewną wartośćvar i = Optional<Int>.some(28)
Według szybkiego „zero” oznacza brak wartości. I utworzyć instancję zainicjowany ze
nil
Musimy być zgodne z protokołem o nazwieExpressibleByNilLiteral
i wielkiej jeśli zgadłeś, tylkoOptionals
zgodneExpressibleByNilLiteral
i zgodne z innymi typami nie jest zalecane.ExpressibleByNilLiteral
ma jedną wywoływaną metodę,init(nilLiteral:)
która inicjuje instancję zerem. Zwykle nie wywołujesz tej metody i zgodnie z szybką dokumentacją odradza się wywoływanie tego inicjalizatora bezpośrednio, ponieważ kompilator wywołuje go za każdym razem, gdy inicjujesz typ opcjonalny za pomocąnil
literału.Nawet ja muszę owinąć (bez słów) moją głowę wokół Opcjonalnie: D Happy Swfting All .
źródło
Po pierwsze, powinieneś wiedzieć, co to jest wartość opcjonalna. Aby uzyskać szczegółowe informacje, możesz przejść do języka programowania Swift .
Po drugie, powinieneś wiedzieć, że opcjonalna wartość ma dwa statusy. Jedna to pełna wartość, a druga zero. Dlatego przed wdrożeniem wartości opcjonalnej powinieneś sprawdzić, który to stan.
Można użyć
if let ...
alboguard let ... else
i tak dalej.Innym sposobem jest to, że jeśli nie chcesz sprawdzać stanu zmiennej przed implementacją, możesz również użyć
var buildingName = buildingName ?? "buildingName"
.źródło
Miałem ten błąd raz, gdy próbowałem ustawić moje wartości gniazd w metodzie przygotowania do segue w następujący sposób:
Potem dowiedziałem się, że nie mogę ustawić wartości gniazd kontrolera docelowego, ponieważ kontroler nie został jeszcze załadowany ani zainicjowany.
Więc rozwiązałem to w ten sposób:
Kontroler miejsca docelowego:
Mam nadzieję, że ta odpowiedź pomoże każdemu, kto ma ten sam problem, ponieważ uznałem, że zaznaczona odpowiedź jest doskonałym źródłem wiedzy na temat opcji i ich działania, ale nie rozwiązała bezpośrednio tego problemu.
źródło
updateView
w kontrolerze docelowym;)updateView
kontrolera docelowego nie jest w tym przypadku potrzebne, ponieważ używamname
zmiennej, aby ustawićnameLabel.text
wviewDidLoad
. Ale gdybyśmy przeprowadzali wiele instalacji, z pewnością lepiej byłoby stworzyć inną funkcję, wykonując to i wywołując jąviewDidLoad
zamiast tego.Zasadniczo próbowałeś użyć wartości zerowej w miejscach, w których Swift zezwala tylko na wartości inne niż zero, mówiąc kompilatorowi, aby ci ufał, że nigdy nie będzie żadnej wartości zerowej, umożliwiając w ten sposób kompilacji aplikacji.
Istnieje kilka scenariuszy, które prowadzą do tego rodzaju krytycznego błędu:
wymuszone rozpakowywanie:
Jeśli
someVariable
jest zero, nastąpi awaria. Wykonując wymuszone rozpakowywanie, przeniosłeś na siebie odpowiedzialność za sprawdzenie zerowe z kompilatora, w zasadzie poprzez wymuszone rozpakowywanie gwarantujesz kompilatorowi, że nigdy nie będziesz mieć żadnych wartości zerowych. I zgadnij, co się stanie, jeśli jakoś zero wyniesiesomeVariable
?Rozwiązanie? Użyj opcjonalnego wiązania (inaczej if-let), wykonaj tam przetwarzanie zmiennych:
wymuszone (w dół) rzuty:
Tutaj przez rzucanie siłą mówisz kompilatorowi, żeby się nie martwił, ponieważ zawsze będziesz miał tam
Rectangle
instancję. I dopóki to trwa, nie musisz się martwić. Problemy zaczynają się, gdy ty lub twoi koledzy z projektu zaczynacie krążyć wartości inne niż prostokąt.Rozwiązanie? Użyj opcjonalnego wiązania (inaczej if-let), wykonaj tam przetwarzanie zmiennych:
Niejawnie nieopakowane opcje. Załóżmy, że masz następującą definicję klasy:
Teraz, jeśli nikt nie pomyli się z tą
name
właściwością, ustawiając ją nanil
, to działa zgodnie z oczekiwaniami, jednak jeśliUser
jest inicjowany z JSON, który nie maname
klucza, wówczas występuje błąd krytyczny przy próbie użycia właściwości.Rozwiązanie? Nie używaj ich :) O ile nie masz 102% pewności, że właściwość zawsze będzie miała wartość inną niż zero, zanim będzie potrzebna. W większości przypadków konwersja na opcjonalną lub nie opcjonalną będzie działać. Jeśli nie będzie opcjonalny, kompilator pomoże ci również powiedzieć ścieżki kodu, które przegapiłeś, podając wartość tej właściwości
Odłączone lub jeszcze nie podłączone gniazda. Jest to szczególny przypadek scenariusza nr 3. Zasadniczo masz klasę załadowaną przez XIB, której chcesz użyć.
Teraz, jeśli przegapiłeś podłączenie gniazdka z edytora XIB, aplikacja ulegnie awarii, gdy tylko będziesz chciał skorzystać z gniazdka. Rozwiązanie? Upewnij się, że wszystkie gniazda są podłączone. Lub użyj
?
operatora na nichemailTextField?.text = "[email protected]"
. Lub zadeklaruj gniazdko jako opcjonalne, ale w tym przypadku kompilator zmusi cię do rozpakowania całego kodu.Wartości pochodzące z Celu-C, które nie zawierają adnotacji o zerowaniu. Załóżmy, że mamy następującą klasę Objective-C:
Teraz, jeśli nie określono adnotacji o zerowaniu (jawnie lub za pomocą
NS_ASSUME_NONNULL_BEGIN
/NS_ASSUME_NONNULL_END
),name
właściwość zostanie zaimportowana do Swift jakoString!
(opcjonalnie IUO - niejawnie rozpakowane). Gdy tylko jakiś szybki kod będzie chciał użyć tej wartości, zawiesi się, jeśliname
będzie zero.Rozwiązanie? Dodaj adnotacje nullability do kodu Objective-C. Uważaj jednak, kompilator Objective-C jest trochę liberalny, jeśli chodzi o zerowanie, możesz skończyć z zerowymi wartościami, nawet jeśli wyraźnie je oznaczysz jako
nonnull
.źródło
Jest to ważniejszy komentarz i dlatego niejawnie nieopakowane opcje mogą być zwodnicze, jeśli chodzi o debugowanie
nil
wartości.Pomyśl o następującym kodzie: Kompiluje się bez błędów / ostrzeżeń:
Jednak w czasie wykonywania wyświetla następujący błąd: Błąd krytyczny: nieoczekiwanie znaleziono zero podczas rozpakowywania wartości opcjonalnej
Czy możesz mi powiedzieć, który obiekt jest
nil
?Nie możesz!
Pełny kod wyglądałby następująco:
Krótko mówiąc, używając
var address : Address!
jesteś ukrywanie możliwość, że zmienna może byćnil
z innymi czytelnikami. A kiedy się zawiesi, jesteś jak „co do diabła ?! mójaddress
nie jest opcjonalny, więc dlaczego się rozbiję ?!Dlatego lepiej jest pisać jako taki:
Czy możesz mi teraz powiedzieć, który to był obiekt
nil
?Tym razem kod został ci wyjaśniony. Możesz zracjonalizować i pomyśleć, że prawdopodobnie jest to
address
parametr, który został siłą rozpakowany.Pełny kod wyglądałby następująco:
źródło
Błędy
EXC_BAD_INSTRUCTION
ifatal error: unexpectedly found nil while implicitly unwrapping an Optional value
pojawia się najczęściej, gdy zadeklarowałeś scenariusz@IBOutlet
, ale nie masz połączenia z nim .Powinieneś także dowiedzieć się o tym, jak działają Opcjonalne , wspomniane w innych odpowiedziach, ale to jedyny czas, który wydaje mi się najczęściej.
źródło
@IBOutlet
przyczyną tego błędu nie powinien być błąd krytyczny: nieoczekiwanie znaleziono zero podczas niejawnego rozpakowywania opcjonalnej wersji błędu?Jeśli pojawi się ten błąd w CollectionView, spróbuj również utworzyć plik CustomCell i niestandardowy xib.
dodaj ten kod w ViewDidLoad () na mainVC.
źródło
Ten błąd napotkałem podczas tworzenia segmentu z kontrolera widoku tabeli do kontrolera widoku, ponieważ zapomniałem podać niestandardową nazwę klasy dla kontrolera widoku w głównej scenorysie.
Coś prostego, co warto sprawdzić, czy wszystko inne wygląda dobrze
źródło