Zanim będę mógł opisać przypadki użycia opcji niejawnie nieopakowanych, powinieneś już zrozumieć, jakie są opcje i niejawnie nieopakowane opcje w programie Swift. Jeśli nie, polecam najpierw przeczytać mój artykuł na temat opcji
Kiedy używać opcji niejawnie nieopakowanej
Istnieją dwa główne powody, dla których można utworzyć niejawnie nieopakowane opcjonalne. Wszystko to ma związek ze zdefiniowaniem zmiennej, do której nigdy nie będzie dostęp, nil
ponieważ w przeciwnym razie kompilator Swift zawsze zmusi cię do jawnego rozpakowania Opcjonalnego.
1. Stała, której nie można zdefiniować podczas inicjalizacji
Każda stała elementu musi mieć wartość przed zakończeniem inicjalizacji. Czasami stała nie może zostać zainicjalizowana z jej prawidłową wartością podczas inicjalizacji, ale nadal można zagwarantować, że będzie miała wartość przed uzyskaniem dostępu.
Użycie zmiennej opcjonalnej pozwala obejść ten problem, ponieważ opcja jest inicjowana automatycznie, nil
a wartość, którą ostatecznie będzie zawierała, pozostanie niezmienna. Jednak ciągłe rozpakowywanie zmiennej, która na pewno nie jest zerowa, może być uciążliwe. Niejawnie nieopakowane opcje osiągają takie same korzyści jak opcjonalne z dodatkową korzyścią, że nie trzeba ich wszędzie rozpakowywać.
Świetnym przykładem tego jest sytuacja, gdy zmiennej członka nie można zainicjować w podklasie UIView, dopóki widok nie zostanie załadowany:
class MyView: UIView {
@IBOutlet var button: UIButton!
var buttonOriginalWidth: CGFloat!
override func awakeFromNib() {
self.buttonOriginalWidth = self.button.frame.size.width
}
}
W tym przypadku nie można obliczyć oryginalnej szerokości przycisku, dopóki widok nie zostanie załadowany, ale wiadomo, że awakeFromNib
zostanie on wywołany przed jakąkolwiek inną metodą w widoku (inną niż inicjalizacja). Zamiast wymuszać jawne rozpakowywanie wartości bez sensu w całej klasie, możesz zadeklarować ją jako niejawnie nieopakowaną opcję.
2. Gdy aplikacja nie może odzyskać sprawności po zmiennej treści nil
Powinno to być niezwykle rzadkie, ale jeśli aplikacja nie może kontynuować działania, jeśli zmienna jest nil
dostępna, dostęp do niej byłby stratą czasu nil
. Zwykle jeśli masz warunek, który musi być absolutnie prawdziwy, aby aplikacja mogła nadal działać, możesz użyć assert
. Implicitly Unwrapped Opcjonalnie ma wbudowane w nią potwierdzenie zerowe. Nawet wtedy dobrze jest rozpakować opcjonalne i użyć bardziej opisowego potwierdzenia, jeśli jest zero.
Kiedy nie należy używać opcji niejawnie nieopakowanej
1. Zmienne obliczane przez Lazily
Czasami masz zmienną składową, która nigdy nie powinna być zerowa, ale podczas inicjalizacji nie można jej ustawić poprawnej wartości. Jednym z rozwiązań jest użycie opcjonalnie nieopakowanej opcji, ale lepszym sposobem jest użycie leniwej zmiennej:
class FileSystemItem {
}
class Directory : FileSystemItem {
lazy var contents : [FileSystemItem] = {
var loadedContents = [FileSystemItem]()
// load contents and append to loadedContents
return loadedContents
}()
}
Teraz zmienna contents
składowa nie jest inicjowana aż do pierwszego dostępu. Daje to klasie szansę na przejście do właściwego stanu przed obliczeniem wartości początkowej.
Uwaga: może to wydawać się sprzeczne z nr 1 z góry. Należy jednak wprowadzić istotne rozróżnienie. buttonOriginalWidth
Powyżej muszą być ustawione podczas viewDidLoad aby zapobiec nikomu zmieniających przyciski Szerokość przed nieruchomość jest dostępna.
2. Wszędzie gdzie indziej
W przeważającej części należy unikać opcji niejawnie nieopakowanych, ponieważ jeśli zostanie użyte błędnie, cała aplikacja ulegnie awarii, gdy będzie do niej dostęp nil
. Jeśli nie masz pewności, czy zmienna może być zerowa, zawsze domyślnie używaj normalnej Opcjonalnej. Rozpakowanie zmiennej, która z nil
pewnością nigdy nie jest bardzo bolesna.
if someOptional
.hasValue
jest zdefiniowane bezpośrednio w Opcjonalne. Wolę semantykę odhasValue
tych!= nil
. Wydaje mi się, że jest to znacznie bardziej zrozumiałe dla nowych programistów, którzy nie używalinil
innych języków.hasValue
jest o wiele bardziej logiczne niżnil
.hasValue
został wycofany z wersji beta 6. Ash jednak odłożył go z powrotem ... github.com/AshFurrow/hasValueNiejawnie nieopakowane opcje są przydatne do przedstawienia właściwości jako nie opcjonalnej, gdy tak naprawdę musi być opcjonalna pod przykryciem. Jest to często konieczne do „wiązania węzła” między dwoma powiązanymi obiektami, z których każdy potrzebuje odniesienia do drugiego. Ma to sens, gdy żadne odwołanie nie jest w rzeczywistości opcjonalne, ale jeden z nich musi być zerowy podczas inicjowania pary.
Na przykład:
Każde
B
wystąpienie powinno zawsze mieć prawidłowemyBuddyA
odwołanie, więc nie chcemy, aby użytkownik traktował je jako opcjonalne, ale potrzebujemy, aby było opcjonalne, abyśmy mogli skonstruowaćB
przed nimA
.JEDNAK! Tego rodzaju wymóg wzajemnego odniesienia jest często oznaką ścisłego połączenia i złej konstrukcji. Jeśli okaże się, że polegasz na niejawnie nieopakowanych opcjach, prawdopodobnie powinieneś rozważyć refaktoryzację w celu wyeliminowania wzajemnych zależności.
źródło
@IBOutlet
weak
deklarację i usuwając „bez tworzenia silnego cyklu przechowywania”Niejawnie nieopakowane opcje są pragmatycznym kompromisem, aby uczynić pracę w środowisku hybrydowym, które musi współpracować z istniejącymi strukturami Cocoa i ich konwencjami, a jednocześnie umożliwia stopniową migrację do bezpieczniejszego paradygmatu programistycznego - bez zerowych wskaźników - wymuszonym przez kompilator Swift.
Szybka książka, w rozdziale Podstawy , sekcja Implicitly Unwrapped Optionals mówi:
To wszystko sprowadza się do przypadków użycia, gdzie nie-
nil
-ness właściwości ma siedzibę poprzez konwencję użytkowania, i nie mogą być egzekwowane przez kompilator podczas inicjalizacji klasy. Na przykładUIViewController
właściwości, które są inicjowane z NIB lub Storyboard, gdzie inicjalizacja jest podzielona na osobne fazy, ale po tymviewDidLoad()
można założyć, że właściwości na ogół istnieją. W przeciwnym razie, aby spełnić wymagania kompilatora, trzeba było użyć wymuszonego rozpakowywania , opcjonalnego wiązania lub opcjonalnego łączenia tylko w celu zaciemnienia głównego celu kodu.Powyższa część książki Swift odnosi się również do rozdziału Automatyczne zliczanie odniesień :
Sprowadza się to do dziwactwa, że nie jest językiem zbierającym śmieci, dlatego łamanie cykli zatrzymania spoczywa na tobie jako programistie i niejawnie nieopakowane opcje są narzędziem do ukrycia tego dziwactwa.
Obejmuje to „Kiedy używać niejawnie rozpakowanych opcji w swoim kodzie?” pytanie. Jako twórca aplikacji najczęściej spotykasz je w sygnaturach metod bibliotek napisanych w Objective-C, które nie mają możliwości wyrażania typów opcjonalnych.
Od używania Swift z kakao i Objective-C, sekcja Praca z nilem :
... i dalej leżą tutaj
źródło
Proste, jednowierszowe (lub kilkuliniowe) przykłady nie pokrywają dobrze zachowania opcjonalnego - tak, jeśli zadeklarujesz zmienną i podasz jej wartość od razu, nie ma sensu opcjonalne.
Najlepszym przypadkiem, jaki do tej pory widziałem, jest konfiguracja, która następuje po zainicjowaniu obiektu, a następnie użycie, które „gwarantuje” przestrzeganie tej konfiguracji, np. W kontrolerze widoku:
Wiemy, że
printSize
zostanie wywołany po załadowaniu widoku - jest to metoda akcji podłączona do kontrolki w tym widoku, i upewniliśmy się, że inaczej go nie wywołamy. Możemy więc zaoszczędzić sobie trochę opcjonalnego sprawdzania / wiązania z!
. Swift nie rozpoznaje tej gwarancji (przynajmniej dopóki Apple nie rozwiąże problemu zatrzymania), więc powiesz kompilatorowi, że istnieje.Jednak w pewnym stopniu narusza to bezpieczeństwo typu. Dowolne miejsce, w którym masz domyślnie nieopakowane opcjonalne, jest miejscem, w którym aplikacja może się zawiesić, jeśli Twoja „gwarancja” nie zawsze się utrzymuje, więc jest to funkcja, której należy używać oszczędnie. Poza tym ciągłe używanie
!
sprawia, że brzmi to tak, jakbyś krzyczał, a nikomu się to nie podoba.źródło
CGSizeZero
może być dobrą wartością wartownika w rzeczywistym użyciu. Ale co, jeśli masz rozmiar załadowany ze stalówki, który może faktycznie wynosić zero? Zatem użycieCGSizeZero
jako wartownika nie pomaga w rozróżnieniu między wartością nieuzbrojoną a ustawioną na zero. Co więcej, odnosi się to równie dobrze do innych typów ładowanych ze stalówki (lub gdziekolwiek indziej poinit
): ciągów, odniesień do widoków podrzędnych itp.let foo? = 42
Raczej nielet foo! = 42
). To nie dotyczy tego. (Może to być odpowiednia odpowiedź na temat opcji, pamiętaj, ale nie na temat niejawnie rozpakowanych opcji, które są innym / powiązanym zwierzęciem).Apple podaje świetny przykład w Swift Programming Language -> Automatyczne zliczanie referencji -> Rozwiązywanie silnych cykli referencyjnych między instancjami klasy -> Nieznane referencje i niejawnie nieopakowane właściwości opcjonalne
źródło
Uzasadnienie ukrytych opcji jest łatwiejsze do wyjaśnienia, najpierw analizując uzasadnienie wymuszonego rozpakowywania.
Wymuszone rozpakowanie opcjonalne (niejawne lub nie) za pomocą! operator oznacza, że masz pewność, że Twój kod nie zawiera błędów, a opcjonalny ma już wartość, w której jest rozpakowywany. Bez ! operator, prawdopodobnie po prostu powiedziałbyś z opcjonalnym powiązaniem:
co nie jest tak miłe jak
Teraz niejawne opcje pozwalają wyrazić posiadanie opcjonalnego, który, jak się spodziewasz, będzie zawsze miał wartość po rozpakowaniu, we wszystkich możliwych przepływach. Więc idzie o krok dalej, pomagając ci - zmniejszając wymóg pisania! za każdym razem rozpakowywać, a także upewnić się, że środowisko wykonawcze nadal będzie zawierało błędy w przypadku błędnych założeń dotyczących przepływu.
źródło
init
(których możesz nawet nie zaimplementować), ale „ re gwarantowane do określenia poawakeFromNib
/viewDidLoad
.Jeśli wiesz na pewno, wartość zwracana z opcjonalnej zamiast z
nil
, niejawnie nieopakowanej opcji używają do bezpośredniego przechwytywania tych wartości z opcji, a opcje nie mogą.To jest różnica między użyciem
let someString : String!
ilet someString : String
źródło
Myślę, że
Optional
to zła nazwa tego konstruktu, która dezorientuje wielu początkujących.Inne języki (na przykład Kotlin i C #) używają tego terminu
Nullable
, co znacznie ułatwia jego zrozumienie.Nullable
oznacza, że możesz przypisać wartość zerową do zmiennej tego typu. Jeśli takNullable<SomeClassType>
, możesz przypisać do niego wartości zerowe, jeśli tak jestSomeClassType
, nie możesz. Tak właśnie działa Swift.Po co z nich korzystać? Czasami trzeba mieć wartości zerowe, dlatego. Na przykład, jeśli wiesz, że chcesz mieć pole w klasie, ale nie możesz przypisać go do niczego podczas tworzenia instancji tej klasy, ale później. Nie podam przykładów, ponieważ ludzie już je tutaj podali. Piszę to, żeby dać moje 2 centy.
Przy okazji, sugeruję przyjrzeć się, jak to działa w innych językach, takich jak Kotlin i C #.
Oto link wyjaśniający tę funkcję w Kotlin: https://kotlinlang.org/docs/reference/null-safety.html
Inne języki, takie jak Java i Scala, mają
Optional
s, ale działają inaczej niżOptional
sw swift, ponieważ typy Java i Scala są domyślnie zerowane.Podsumowując, uważam, że ta funkcja powinna zostać nazwana
Nullable
w Swift, a nieOptional
...źródło
Implicitly Unwrapped Optional
jest cukrem składniowymOptional
, ponieważ nie zmusza programisty do rozpakowywania zmiennej. Może być użyty do zmiennej, której nie można zainicjować podczastwo-phase initialization process
i sugeruje, że nie ma wartości zero. Ta zmienna zachowuje się jako nie-zero, ale w rzeczywistości jest zmienną opcjonalną . Dobrym przykładem jest - Outlety Konstruktora interfejsówOptional
zwykle są lepszeźródło