NEW: Join my free 100 Days of SwiftUI challenge today! >>

Typy wyliczeniowe (z ang. Enumerations)

Typy wyliczeniowe, najczęściej nazywane po prostu "enumami", są sposobem na zdefiniowanie Twojego własnego rodzaju wartości w języku Swift. W niektórych językach programowania są one proste i nieskomplikowane. Swift dodaje do nich dużo możliwości jeśli zdecydujesz się wyjść poza ich podstawy.

Zacznijmy od prostego przykładu z wcześniej:

func getHaterStatus(weather: String) -> String? {
    if weather == "sunny" {
        return nil
    } else {
        return "Hate"
    }
}

Funkcja powyżej przyjmuje ciąg znaków, który definiuje aktualną pogodę. Problem w tym, że ciąg znaków nie jest dobrym wyborem do tego typu danych - powinniśmy użyć "deszcz", "pada" czy "deszczowo"? A może "ulewa", "mżawka" lub "oberwanie chmury"? Co gdy jedna osoba napisze "Deszcz" z dużej litery a kolejna napisze "Desz", ponieważ nie patrzyła co wpisywała na klawiaturze?

Typy wyliczeniowe (z ang. Enums), dalej nazywane enumami, rozwiązują ten problem pozwalając Ci na zdefiniowanie nowego typu danych, a następnie zadeklarowanie potencjalnych wartości, które ten typ będzie mógł przyjąć. Przykładowo, możemy powiedzieć, że jest pięć rodzajów pogody: słońce, chmury, deszcz, wiatr i śnieg. Jeśli stworzymy z tego enum, będzie to oznaczać, że Swift przyjmie tylko te pięć wartości - każda inna spowoduje błąd. Warto zwrócić uwagę, że pod maską enumy to najczęściej proste liczby, z którymi, w porównaniu do ciągów znaków, komputery współpracują szybciej.

Spróbujmy zamienić to w kod:

enum WeatherType {
    case sun, cloud, rain, wind, snow
}

func getHaterStatus(weather: WeatherType) -> String? {
    if weather == WeatherType.sun {
        return nil
    } else {
        return "Hate"
    }
}

getHaterStatus(weather: WeatherType.cloud)

Spójrz na pierwsze trzy linie: pierwsza nadaje naszemu typowi nazwę - WeatherType. Później użyjesz go w swoim kodzie zamiast typu String czy Int. Linia druga deklaruje pięć możliwych wartości jakie może przyjąć nasz enum. Przez wzgląd na konwencję, każdą z wartości zaczynamy małą literą. Linia trzecia jest tylko klamrą zamykającą, kończącą naszą deklarację.

Teraz spójrz jak został użyty: zmodyfikowałem gethaterStatus() tak, że przyjmuje wartość typu WeatherType. Przepisałem również warunkową instrukcję by przyrównywała do WeatherType.sun, czyli jednej z naszych wartości. Pamiętaj, to przyrównanie jest pod spodem tylko liczbą, więc jest bardzo szybkie.

Teraz, przeczytaj powyższy kod ponownie, ponieważ wprowadzę do niego dwie bardzo ważne zmiany. Gotowy?

enum WeatherType {
    case sun
    case cloud
    case rain
    case wind
    case snow
}

func getHaterStatus(weather: WeatherType) -> String? {
    if weather == .sun {
        return nil
    } else {
        return "Hate"
    }
}

getHaterStatus(weather: .cloud)

Możesz zauważyć dwie zmiany. Pierwsza - każdy z typów pogody jest w oddzielnej linii. To może się wydawać nieznaczącą zmianą i rzeczywiście w tym przykładzie taką jest, ale stanie się bardzo ważna już wkrótce. Drugą jest fakt, że napisałem if weather == .sun - nie musiałem jawnie wskazać, że miałem na myśli WeatherType.sun, ponieważ Swift wie, że przyrównuje do zmiennej typu WeatherType, więc potrafi sam wywnioskować typ (z ang. type inference).

Enumy są szczególnie przydatne w blokach switch/case dlatego, że język Swift zna wszystkie możliwe wartości Twojego enuma, więc może się upewnić, że pokryjesz wszystkie jego przypadki. Dla przykładu możemy spróbować przepisać funkcję getHaterStatus() na:

func getHaterStatus(weather: WeatherType) -> String? {
    switch weather {
    case .sun:
        return nil
    case .cloud, .wind:
        return "dislike"
    case .rain:
        return "hate"
    }
}

Tak, domyślam się, że "haters gonna dislike" nie jest najlepszym sloganem na świecie, ale jest to mało ważne bo kod i tak się nie skompiluje: nie pokrywa przypadku .snow, a język Swift chcę pokryć wszystkie przypadki. Musisz obsłużyć ten przypadek albo dopisać przypadek domyślny (z ang. default case).

Typy wyliczeniowe z dodatkowymi wartościami (z ang. Enumarations with associated values)

Jedną z najpotężniejszych cech języka Swift, jest fakt, że typy wyliczeniowe mogą posiadać przypisane im wartości, które to Ty możesz zdefiniować. Aby rozbudować nasz rosnąco niejasny przykład jeszcze bardziej, dodam wartość do przypadku .wind abyśmy mogli zadeklarować prędkość wiatru. Zmodyfikuj kod do poniższej postaci:

enum WeatherType {
    case sun
    case cloud
    case rain
    case wind(speed: Int)
    case snow
}

Jak widzisz, inne przypadki nie potrzebują wartości prędkości - dodałem ją tylko do przypadku .wind. Teraz zaczyna się magia: język Swift pozwala nam na dodanie dodatkowych warunków do bloku switch/case tak aby przypadek zgadzał się tylko wtedy gdy dane warunki zostaną spełnione. Poniższy przykład używa słowa kluczowego let aby dostać się do wartości znajdującej się w przypadku, a następnie słowa kluczowego where w celu przyrównania.

Oto nowa funkcja:

func getHaterStatus(weather: WeatherType) -> String? {
    switch weather {
    case .sun:
        return nil
    case .wind(let speed) where speed < 10:
        return "meh"
    case .cloud, .wind:
        return "dislike"
    case .rain, .snow:
        return "hate"
    }
}

getHaterStatus(weather: WeatherType.wind(speed: 5))

Zauważ, że .wind pojawia się powyżej dwa razy, ale za pierwszym razem ten warunek będzie prawdziwy tylko wtedy gdy prędkość wiatru będzie mniejsza niż 10 kilometrów na godzinę. Jeśli prędkość będzie równa 10 albo więcej, warunek nie zostanie spełniony. Kluczowe jest tutaj użycie let aby móc użyć wartości w środku enuma (tzn. zdeklarować nazwę stałej, do której możesz się odnieść) a potem użycie warunku where do przyrównania.

Język Swift sprawdza blok switch/case od góry do dołu i zatrzymuje się gdy tylko znajdzie pasujący warunek. To oznacza, że gdyby case .cloud, .wind: pojawiłoby się przed case .wind(let speed) where speed < 10 to wykonałby się pierwszy warunek, a co za tym idzie - zmieniłby się wynik.

Dlatego zwracaj szczególną uwagę na kolejność swoich warunków!

Wskazówka: Typy opcjonalne w języku Swift są zaimplementowane przy użyciu typów wyliczeniowych z przypisanymi wartościami. Posiadają dwa przypadki: none oraz some, gdzie some zawiera w sobie wartość odpowiednią dla danego typu opcjonalnego.

SPONSORED Instabug helps you identify and resolve severe crashes quickly. You can retrace in-app events and know exactly which line of code caused the crash along with environment details, network logs, repro steps, and the session profiler. Ask more questions or keep users up-to-date with in-app replies straight from your dashboard. Instabug takes data privacy seriously, so no one sees your data but you! See more detailed features comparison and try Instabug's crash reporting SDK for free.

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!