Failable inicjalizatory (init?) w Swift pozwalają opisać sytuację, gdy utworzenie instancji typu może zakończyć się niepowodzeniem i zwrócić nil. Często używa się ich do walidacji danych wejściowych lub konwersji, które mogą nie zakończyć się sukcesem. W failable inicjalizatorze można jawnie zwrócić nil, wskazując na nieudane utworzenie obiektu.
Przykład:
struct User { let name: String let age: Int init?(name: String, age: Int) { guard !name.isEmpty, age > 0 else { return nil } self.name = name self.age = age } }
Dzięki temu można zapobiegać tworzeniu nieprawidłowych obiektów.
compactMap), jest to wygodne do filtrowania nieprawidłowych instancji.Pytanie: Czym różni się init? od init! i kiedy używać failable inicjalizatora z implicit unwrapping?
Odpowiedź: init? zwraca opcjonal (<type?>), a jeżeli inicjalizacja się nie powiodła, zwróci nil, co wymaga bezpiecznego przetwarzania. init! zwraca implicitly unwrapped optional (<type!>), a jeżeli inicjalizacja się nie powiodła, również zwróci nil, ale użycie takiego obiektu bez sprawdzenia spowoduje runtime-crash. Używaj init! tylko wtedy, gdy masz pewność, że inicjalizacja nie może zakończyć się niepowodzeniem w Twoim kontekście (na przykład podczas pracy z storyboardem w UIKit).
let value = Int("abc") // value będzie nil
Historia
Podczas ręcznego parsowania JSON użyto zwykłego inicjalizatora zamiast failable. Doprowadziło to do stworzenia "pustych" użytkowników, ponieważ walidacja nie zadziałała i aplikacja nie filtrowała nieprawidłowych danych.
Historia
Użycie
init!z potencjalnie nieprawidłowymi danymi doprowadziło do awarii aplikacji po aktualizacji API: format danych wejściowych uległ zmianie, a w momencie wydobywania obiektu wystąpił wyjątek runtime z powodu niejawnego rozpakowywania nil.
Historia
Przy własnej implementacji failable init zapomniano jawnie zwrócić nil dla niektórych scenariuszy, co w efekcie doprowadziło do zainicjowania struktury z "brudnymi" polami, co później spowodowało błędy w logice biznesowej.