Sendable — je Swift protokol, který označuje typy bezpečné pro přenos mezi vlákny. Při práci s konkurencí (actor, async/await, Task) Swift kompilátor vyžaduje, aby všechna data přenášená mezi izolovanými kontexty byla v souladu se Sendable. To vylučuje náhodný přenos nebezpečných typů, které mohou vést k datovým závodům. Protokol funguje jako smlouva: typ, který je v souladu se Sendable, zaručuje absenci vnitřního nesynchronizovaného stavu. Podle WWDC 2021 je protokol Sendable povinným prvkem při navrhování bezpečné vícevláknové architektury.
Hlavní
Sendable — je protokol ze standardní knihovny Swift (SE-0302), který označuje typy bezpečné pro přenos mezi doménami izolace. Doménou izolace se rozumí actor, úloha Task nebo kontext @MainActor. Sendable neobsahuje požadavky — funguje jako bezpečnostní značka.
Před Swift 5.5 mohli vývojáři přenášet libovolný objekt mezi vlákny přes DispatchQueue a kompilátor nekontroloval, zda je to bezpečné. Sendable uzavírá tuto mezeru: nyní kompilátor sám sleduje mezipřenosy mezi vlákny a blokuje nebezpečné. To činí souběžný kód spolehlivějším ještě před spuštěním.
struct UserProfile: Sendable {
let name: String
let age: Int
}
class NonSendableClass {
var counter: Int = 0
}
V příkladu může být UserProfile bezpečně přenesen mezi vlákny, protože se jedná o strukturu s konstantními vlastnostmi Sendable typů. NonSendableClass způsobí chybu kompilace při pokusu o přenos přes await nebo do Task.
Sendable typy v Swift se dělí do tří kategorií: hodnotové typy s vlastnostmi Sendable, finální třídy s neměnným stavem a funkce/uzávěry označené @Sendable. Kompilátor automaticky odvozuje soulad se Sendable pro hodnotové typy.
Struct, enum a tuple se automaticky stávají Sendable, pokud všechny jejich vlastnosti a přidružené hodnoty jsou také Sendable. Toto je konzervativní přístup: pokud alespoň jedna vlastnost není v souladu se Sendable, celý typ nebude považován za bezpečný.
| Typ | Sendable | Podmínka |
|---|---|---|
| Struct | Ano (bez explicitního uvedení) | Všechny vlastnosti Sendable |
| Enum | Ano (bez explicitního uvedení) | Všechny přidružené hodnoty Sendable |
| Final class | Ano (s explicitním uvedením) | Všechny let vlastnosti Sendable, žádné var |
| Non-final class | Ne | Nemůže být Sendable kvůli dědičnosti |
Všechny vestavěné Swift typy — Int, String, Double, Bool, Optional, Array, Dictionary, Set — jsou v souladu se Sendable. To činí kompozici typů implicitně bezpečnou. Vývojář se musí starat pouze o vlastní třídy.
@unchecked Sendable — je mechanismus umožňující třídě explicitně se prohlásit za Sendable, obcházející kontroly kompilátoru. Vývojář přebírá odpovědnost za bezpečnost vláken takové třídy. To je užitečné pro mosty Objective-C a optimalizované struktury.
@unchecked Sendable se aplikuje, když třída interně zaručuje bezpečnost prostřednictvím zámků nebo atomických operací, ale kompilátor to nemůže staticky zkontrolovat. Například třída s os_unfair_lock nebo pthread_mutex_t — její bezpečnost vláken je zajištěna kódem, ale Swift to nevidí.
final class AtomicCounter: @unchecked Sendable {
private var value: Int = 0
private let lock = NSLock()
func increment() {
lock.lock()
value += 1
lock.unlock()
}
}
@unchecked Sendable používejte opatrně. Toto je jasný signál pro ostatní vývojáře: „Zkontroloval jsem, je to bezpečné”. Chyba v implementaci může vést k obtížně odhalitelným závodům. Před použitím @unchecked se ujistěte, že typ opravdu nelze přepsat jako hodnotový typ.
Actor a Sendable jsou dvě strany téže mince. Actor izoluje svůj stav, ale pro výměnu dat s vnějším světem musí vracet Sendable typy. Pokud metoda actor vrací ne-Sendable typ, kompilátor vydá varování nebo chybu.
Když actor posílá data externímu kódu, tato data překračují hranici izolace. Sendable zaručuje, že příjemce může bezpečně používat mimo actor. Samotný actor zůstává izolovaný — jeho vnitřní stav není odhalen.
struct AccountSnapshot: Sendable {
let id: UUID
let balance: Double
let lastUpdated: Date
}
actor BankActor {
private var balance: Double = 0
func snapshot() async -> AccountSnapshot {
return AccountSnapshot(
id: UUID(),
balance: balance,
lastUpdated: Date()
)
}
}
AccountSnapshot — je Sendable struktura, která obsahuje pouze let vlastnosti Sendable typů. Tento přístup je best practice pro extrakci dat z actor. Snímek stavu je přenášen hodnotou a actor neztrácí kontrolu nad svým stavem.
@Sendable — je atribut pro funkce a uzávěry, který zaručuje, že uzávěr nezachycuje ne-Sendable data měnitelným způsobem. Když je uzávěr předán do Task nebo metody actor, musí být Sendable.
Kompilátor kontroluje, že @Sendable uzávěr nezachycuje měnítelné reference na třídy. Zachycení let vlastností Sendable typů je povoleno. Zachycení var proměnné referenčního typu způsobí chybu, protože uzávěr může být proveden současně s mutací.
func performAsync(operation: @Sendable () -> Void) {
Task {
await operation()
}
}
let constant = "Safe"
var counter = 0
// counter — zachycení var mutuje počítadlo, chyba kompilace
performAsync { // ❌ Mutace zachyceného var
print(constant)
}
Pravidlo je jednoduché: @Sendable uzávěr může zachytit pouze ta data, která jsou sama Sendable a nebudou mutována zvenčí. Pro třídy je povoleno zachycení slabé reference weak self, pokud třída není označena jako Sendable. To zabraňuje klasickým retain cycle a race condition.
Často kladené otázky
Sendable — je značka, která říká kompilátoru: „Tento typ lze bezpečně přenášet mezi vlákny”. Struktury a konstanty jsou obvykle implicitně Sendable, třídy — ne, pokud to není výslovně uvedeno.
Hodnotové typy (struct, enum) s vlastnostmi Sendable, finální třídy s neměnným stavem, stejně jako všechny základní Swift typy: Int, String, Double, Bool, Array, Dictionary, Optional — automaticky splňují Sendable.
@unchecked Sendable — je způsob, jak prohlásit třídu za Sendable bez statické kontroly kompilátorem. Vývojář zaručuje bezpečnost samostatně, často prostřednictvím zámků nebo atomických operací.
Actor izoluje svůj stav, ale při vracení dat externímu kódu tato data překračují hranici izolace. Sendable zaručuje, že příjemce nenarazí na závody při používání těchto dat mimo actor.
Přidejte protokol Sendable k finální třídě, jejíž všechny vlastnosti jsou konstanty Sendable typů. Pokud třída používá zámky, lze aplikovat @unchecked Sendable, ale to vyžaduje opatrnost.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také