Sendable — co to jest, protokół bezpieczeństwa wątków

Autor: IT Sectr Opublikowano: 2026-06-18 Czas czytania: 10 min

Sendable — to protokół Swifta, który oznacza typy bezpieczne do przesyłania między wątkami. Podczas pracy ze współbieżnością (actor, async/await, Task) kompilator Swift wymaga, aby wszystkie dane przesyłane między izolowanymi kontekstami były zgodne z Sendable. Eliminuje to przypadkowe przesyłanie niebezpiecznych typów, które mogą prowadzić do wyścigów danych. Protokół pełni rolę kontraktu: typ zgodny z Sendable gwarantuje brak wewnętrznego niesynchronizowanego stanu. Według WWDC 2021, protokół Sendable jest obowiązkowym elementem przy projektowaniu bezpiecznej architektury wielowątkowej.

Najważniejsze

  • Sendable — protokół gwarantujący brak wyścigów danych przy przesyłaniu między wątkami
  • Typy wartościowe (struct, enum) automatycznie są zgodne z Sendable, jeśli wszystkie ich właściwości są Sendable
  • @unchecked Sendable — mechanizm obejścia dla klas, których bezpieczeństwo deweloper bierze na siebie
  • Zamykania Sendable są oznaczane atrybutem @Sendable, który sprawdza przechwytywanie zmiennych
  • Kompilator zgłasza błąd, jeśli typ nie-Sendable jest przesyłany między izolowanymi kontekstami

Co to jest protokół Sendable w Swift?

Sendable — to protokół ze standardowej biblioteki Swift (SE-0302), który oznacza typy bezpieczne do przesyłania między domenami izolacji. Domeną izolacji jest actor, zadanie Task lub kontekst @MainActor. Sendable nie zawiera wymagań — pełni rolę znacznika bezpieczeństwa.

Po co jest Sendable

Przed Swift 5.5 deweloperzy mogli przesłać dowolny obiekt między wątkami przez DispatchQueue, a kompilator nie sprawdzał, czy jest to bezpieczne. Sendable zamyka tę lukę: teraz kompilator sam śledzi przesłania międzywątkowe i blokuje niebezpieczne. To sprawia, że kod współbieżny jest bardziej niezawodny jeszcze przed uruchomieniem.

swift
struct UserProfile: Sendable {
    let name: String
    let age: Int
}

class NonSendableClass {
    var counter: Int = 0
}

W przykładzie UserProfile może być bezpiecznie przesłany między wątkami, ponieważ jest to struktura ze stałymi właściwościami typów Sendable. NonSendableClass spowoduje błąd kompilacji przy próbie przesłania przez await lub do Task.

Które typy są domyślnie zgodne z Sendable

Typy Sendable w Swift dzielą się na trzy kategorie: typy wartościowe z właściwościami Sendable, finalne klasy z niezmiennym stanem oraz funkcje/zamykania oznaczone @Sendable. Kompilator automatycznie wyprowadza zgodność z Sendable dla typów wartościowych.

Typy wartościowe i automatyczna zgodność

Struct, enum i tuple automatycznie stają się Sendable, jeśli wszystkie ich właściwości i wartości stowarzyszone również są Sendable. To konserwatywne podejście: jeśli choć jedna właściwość nie jest zgodna z Sendable, cały typ nie zostanie uznany za bezpieczny.

TypSendableWarunek
StructTak (bez jawnego wskazania)Wszystkie właściwości Sendable
EnumTak (bez jawnego wskazania)Wszystkie associated values Sendable
Final classTak (z jawnym wskazaniem)Wszystkie let-właściwości Sendable, brak var
Non-final classNieNie może być Sendable z powodu dziedziczenia

Typy podstawowe

Wszystkie wbudowane typy Swift — Int, String, Double, Bool, Optional, Array, Dictionary, Set — są zgodne z Sendable. To sprawia, że kompozycja typów jest domyślnie bezpieczna. Deweloper musi jedynie uważać na niestandardowe klasy.

Sendable a klasy: @unchecked Sendable

@unchecked Sendable — to mechanizm pozwalający klasie jawnie zadeklarować się jako Sendable, z pominięciem sprawdzania przez kompilator. Deweloper bierze na siebie odpowiedzialność za bezpieczeństwo wątkowe takiej klasy. Jest to przydatne dla mostów Objective-C i zoptymalizowanych struktur.

Kiedy używać @unchecked Sendable

@unchecked Sendable stosuje się, gdy klasa wewnętrznie gwarantuje bezpieczeństwo poprzez blokady lub operacje atomowe, ale kompilator nie może tego statycznie sprawdzić. Na przykład klasa z os_unfair_lock lub pthread_mutex_t — jej bezpieczeństwo wątkowe jest zapewnione przez kod, ale Swift tego nie widzi.

swift
final class AtomicCounter: @unchecked Sendable {
    private var value: Int = 0
    private let lock = NSLock()
    
    func increment() {
        lock.lock()
        value += 1
        lock.unlock()
    }
}

Należy używać @unchecked Sendable ostrożnie. To wyraźny sygnał dla innych deweloperów: „s­prawd­ziłem, to jest bezpieczne”. Błąd w implementacji może prowadzić do trudnych do wykrycia wyścigów. Przed zastosowaniem @unchecked warto upewnić się, że typ naprawdę nie może być przepisany jako typ wartościowy.

Sendable w połączeniu z Actor

Actor i Sendable to dwie strony tego samego medalu. Actor izoluje swój stan, ale do wymiany danych ze światem zewnętrznym musi zwracać typy Sendable. Jeśli metoda actor zwraca typ nie-Sendable, kompilator zgłasza ostrzeżenie lub błąd.

Przesyłanie danych przez Sendable

Gdy actor wysyła dane do kodu zewnętrznego, dane te przekraczają granicę izolacji. Sendable gwarantuje, że odbiorca może bezpiecznie używać ich poza aktorem. Sam actor pozostaje izolowany — jego wewnętrzny stan nie jest ujawniany.

swift
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 — to struktura Sendable, która zawiera tylko let-właściwości typów Sendable. Takie podejście to best practice przy wyodrębnianiu danych z actor. Migawka stanu jest przesyłana przez wartość, a actor nie traci kontroli nad swoim stanem.

Funkcje i zamykania Sendable

@Sendable — to atrybut dla funkcji i zamykań, który gwarantuje, że zamykanie nie przechwytuje nie-Sendable danych w zmienialny sposób. Gdy zamykanie jest przesyłane do Task lub metody actor, musi być Sendable.

Sprawdzanie przechwytywania w @Sendable zamykaniach

Kompilator sprawdza, czy @Sendable zamykanie nie przechwytuje zmienialnych referencji do klas. Przechwytywanie let-właściwości typów Sendable jest dozwolone. Przechwytywanie var-zmiennej typu referencyjnego spowoduje błąd, ponieważ zamykanie może być wykonane jednocześnie z mutacją.

swift
func performAsync(operation: @Sendable () -> Void) {
    Task {
        await operation()
    }
}

let constant = "Safe"
var counter = 0
// counter — przechwytywanie var mutuje licznik, błąd kompilacji
performAsync { // ❌ Mutacja przechwyconej var
    print(constant)
}

Zasada jest prosta: @Sendable zamykanie może przechwytywać tylko te dane, które same są Sendable i nie będą mutowane z zewnątrz. Dla klas dozwolone jest przechwytywanie słabej referencji weak self, jeśli klasa nie jest oznaczona jako Sendable. Zapobiega to klasycznym cyklom retain i wyścigom.

Często zadawane pytania

Co to jest Sendable w Swift prostymi słowami?

Sendable — to znacznik, który mówi kompilatorowi: „ten typ można bezpiecznie przesyłać między wątkami”. Struktury i stałe są zwykle Sendable domyślnie, klasy — nie, chyba że zostało to jawnie określone.

Które typy są automatycznie zgodne z Sendable?

Typy wartościowe (struct, enum) z właściwościami Sendable, finalne klasy z niezmiennym stanem, a także wszystkie podstawowe typy Swift: Int, String, Double, Bool, Array, Dictionary, Optional — są automatycznie zgodne z Sendable.

Co to jest @unchecked Sendable?

@unchecked Sendable — to sposób na zadeklarowanie klasy jako Sendable bez statycznego sprawdzania przez kompilator. Deweloper gwarantuje bezpieczeństwo samodzielnie, często poprzez blokady lub operacje atomowe.

Po co Sendable jest potrzebny dla Actor?

Actor izoluje swój stan, ale przy zwracaniu danych do kodu zewnętrznego dane te przekraczają granicę izolacji. Sendable gwarantuje, że odbiorca nie napotka wyścigów przy używaniu tych danych poza aktorem.

Jak zrobić klasę Sendable w Swift?

Dodaj protokół Sendable do final-klasy, której wszystkie właściwości są stałymi typów Sendable. Jeśli klasa używa blokad, można zastosować @unchecked Sendable, ale wymaga to ostrożności.

Podsumowanie

  • Sendable — protokół-znacznik do bezpiecznego przesyłania danych między wątkami
  • Typy wartościowe automatycznie są zgodne z Sendable przy spełnieniu warunków
  • Klasy wymagają jawnego wskazania Sendable lub @unchecked Sendable
  • Actor zwraca dane do kodu zewnętrznego tylko przez typy Sendable
  • @Sendable atrybut zamykań sprawdza poprawność przechwytywania zmiennych
  • Kompilator statycznie sprawdza zgodność z Sendable, zapobiegając wyścigom na etapie kompilacji

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również