Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 6 - Typový systém: Optionals ve Swift

V předešlém cvičení, Řešené úlohy k 5. lekci Swift, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

Nyní si konečně vysvětlíme koncept tzv. Optionals, který je ve Swift velmi důležitý. Řekneme si, co znamenají všechny ty vykřičníky ve zdrojovém kódu. S několika jsme se už setkali a byť se může pro začátek jednat o složitější koncept, tak bude lepší, když budete alespoň tušit, o co se jedná.

Koncept hodnoty nil

Programovací jazyky se musí nějak vypořádat se situací, kdy proměnná nemá žádnou hodnotu. S takovým problémem se často setkáváme u funkcí, které se nemusí provést korektně. Pokud se např. nepodaří načtení čísla z konzole, nemělo by být vráceno žádné číslo, ale "prázdno". Pokud by nám Swift vrátil v tomto případě např. hodnotu 0 nebo -1, nepoznali bychom zda se číslo nepodařilo načíst nebo zda uživatel vložil právě 0 nebo -1. Za tímto účelem se vymyslela speciální hodnota nil, která bezpečně označí, že je proměnná prázdná. V ostatních programovacích jazycích se tato hodnota často jmenuje null a funguje úplně stejně.

Pokud si ve Swift vytvoříme standardní proměnnou, prázdnou hodnotu nil do ní přiřadit nelze:

// Tento kód je chybný
var cislo = 15
cislo = nil // Tento řádek vyvolá chybu

Někdo by ji tam totiž nemusel očekávat. Abychom nil mohli do proměnné přiřadit, musíme proměnnou označil jako Optional.

Optionals

Typ Optional můžeme chápat jako jakýsi box, který slouží k zabalení obyčejné proměnné. Box vždy existuje, po jeho otevření ale hodnotu buď najdeme nebo je prázdný.

Otazník

Optional typ vytvoříme tak, že za název datového typu proměnné umístíme otazník ?. Zkusme si to:

var moznaCislo: Int? = 15
moznaCislo = nil

Kód se již přeložil v pořádku a proměnná moznaCislo je nyní prázdná, i když se jedná o číslo. To zní zatím dobře, že?

Je tu ovšem problém, který mnoho ostatních programovacích jazyků nedokáže vyřešit. S moznaCislo by nám nyní nemělo být umožněno pracovat jako s obyčejnou proměnnou. Pokud bychom napsali:

var moznaCislo: Int? = 15
moznaCislo = nil
print(moznaCislo  * 2)

a program se přeložil, mohl by za běhu spadnout v případě, že by moznaCislo bylo prázdné. Nemůžeme přeci vynásobit "nezadáno" dvěma. Když si takový program zkusíte napsat, zjistíte, že nejde přeložit. Podobně by nám Swift vynadal i při přístupu k vlastnosti nebo metodě Optional typu. Můžete si zkusit, že kvůli výpisu délky druhého řetězce nepůjde následující kód přeložit:

var s1 = "Ahoj"
var s2: String? = "Světe"
print(s1.count)
print(s2.count)

Asi jste tušili, že Swift nepatří mezi jazyky, které by si tento problém neohlídaly :)

Null safety

Mechanismus, který již při překladu kontroluje jak Optional typy používáme, se často nazývá null safety. Existuje několik způsobů jak Optional proměnnou použít. Postupně si je vyzkoušíme.

Vykřičník

Optional můžeme vytvořit ještě druhým způsobem. Pomůže nám s tím vykřičník !:

var vzdyCislo : Int!
// Můžeme napsat print(vzdyCislo + 1), ale aplikace spadne, protože v boxu vzdyCislo ještě číslo není

Tímto způsobem Swiftu říkáme, že se o proměnnou postaráme sami a zajistíme, aby měla před přístupem k ní platnou hodnotu (v tomto případě libovolné celé číslo). V kódu s ní můžeme pracovat jako s obyčejným Int, který jinak bez hodnoty nejde deklarovat, ale snadno nám může program spadnout.

Určitě vám došlo, že význam vykřičníků v kódu je nějaké riziko a měli bychom se jim spíše vyhýbat.

Rozbalujeme box

Nyní si konečně vysvětlíme jak se k hodnotám dostat a proč jsme do našich kódu psali vykřičníky.

Operátor !

Začněme tím nejhloupějším, který jsme zatím v kurzu používali, aby toho na nás nebylo ze začátku moc. Pomocí operátoru ! můžeme Swift degradovat na starší jazyky jako je např. Java a kontrolu null safety vypnout. Pokud v proměnné zrovna nebude nil, vše bude fungovat:

var moznaCislo: Int? = 15
print(moznaCislo!  * 2)

Výsledek:

30

V anglické terminologii se pro tuto techniku používá výraz force unwrapping (vynutit rozbalení).

Pokud v proměnné ovšem prázdná hodnota bude, celá aplikace za běhu upadne s chybou.

var moznaCislo: Int? = 15
moznaCislo = nil
print(moznaCislo!  * 2)

Při překladu bychom na tuto chybu vůbec nepřišli. Tento způsob se využívá velmi zřídka a obecně v případech, kdy víme, že vždy bude hodnota nebo naopak její absence znamená, že nemá smysl kód dále vykonávat, protože jde o něco kritického.

Optional binding

K Optionals bychom měli vždy přistupovat opatrně právě skrze optional binding. Používají se dvě hlavní konstrukce, jedna za pomoci tradiční if podmínky a druhá za pomoci slovíčka guard. Jedná se spíše o sémantické rozdíly (mají jiný význam). Nejdříve si je ukážeme a následně vysvětlíme.

Ošetření Optional hodnoty podmínkou
var optionalNumber : Int? = 5
if let number = optionalNumber {
    print(number * 2)
} else {
    print("Číslo není zadané")
}

Kód v podmínce výše se spustí pouze v případě, že proměnná optionalNumber obsahuje hodnotu. Ta se následně uloží do konstanty number a v těle podmínky s ní pracujeme jako s tradičním Int, takže nejsme dále omezováni.

Pro zajímavost: Můžete používat také if var, ale prakticky se to nedělá, protože je daná proměnná stejně aktivní pouze v tomto bloku a nedává smysl s ní moc manipulovat.

guard

Stručně se dá říci, že guard funguje opačně a spustí blok v případě, že podmínka neplatí. Používá se pouze ve funkcích, které zatím neumíme, kde z funkce v bloku vystoupí pomocí příkazu return. Ukážeme si jej až dále v kurzu.

Rozbalení pomocí výchozí hodnoty

Ještě si ukážeme další možnost, jak se zbavit Optional a získat tradiční datový typ. Můžeme totiž jednoduše určit náhradní hodnotu, která se použije, pokud je Optional prázdný. Odborně se označuje jako nil coalescing a disponuje vlastním operátorem ??.

var moznaCislo: Int?
moznaCislo = nil

let urciteCislo = moznaCislo ?? 4

print(urciteCislo)

Můžete vidět, že operátor ?? je binární, tudíž vyžaduje hodnoty na obou stranách. Pokud levá strana není nil (respektive prázdný Optional), tak operátor dále nic neřeší a přiřadí tuto hodnotu. Jestli je ale nil, tak dojde k přiřazení druhé hodnoty.

Zde se nám vypíše 4, protože je proměnná moznaCislo nil. Toto se hodí v případě, že lze rozumně pokračovat také s výchozí hodnotou, kterou přes ?? nastavíme. Tento operátor je možné řetězit, ale pro přehlednost bych to nedoporučoval.

V příští lekci, Pole ve Swift, se budeme věnovat polím.


 

Předchozí článek
Řešené úlohy k 5. lekci Swift
Všechny články v sekci
Základní konstrukce jazyka Swift
Přeskočit článek
(nedoporučujeme)
Pole ve Swift
Článek pro vás napsal Filip Němeček
Avatar
Uživatelské hodnocení:
9 hlasů
Autor se věnuje vývoji iOS aplikací (občas macOS)
Aktivity