Sendable — är ett Swift-protokoll som markerar typer som är säkra att överföra mellan trådar. Vid arbete med konkurrens (actor, async/await, Task) kräver Swift-kompilatorn att all data som överförs mellan isolerade kontexter måste följa Sendable. Detta förhindrar oavsiktlig överföring av osäkra typer som kan leda till datarace. Protokollet fungerar som ett kontrakt: en typ som följer Sendable garanterar frånvaron av internt osynkroniserat tillstånd. Enligt WWDC 2021 är Sendable-protokollet ett obligatoriskt element vid design av säker flertrådad arkitektur.
Huvudsakligt
Sendable — är ett protokoll från Swifts standardbibliotek (SE-0302) som markerar typer som är säkra att överföra mellan isoleringsdomäner. Med isoleringsdomän avses en actor, en Task-uppgift eller en @MainActor-kontext. Sendable innehåller inga krav — det fungerar som en säkerhetsmarkör.
Före Swift 5.5 kunde utvecklare överföra vilket objekt som helst mellan trådar via DispatchQueue, och kompilatorn kontrollerade inte om det var säkert. Sendable täpper till detta hål: nu övervakar kompilatorn själv överföringar mellan trådar och blockerar osäkra sådana. Detta gör konkurrent kod mer tillförlitlig redan innan den körs.
struct UserProfile: Sendable {
let name: String
let age: Int
}
class NonSendableClass {
var counter: Int = 0
}
I exemplet kan UserProfile säkert överföras mellan trådar eftersom det är en struktur med konstanta egenskaper av Sendable-typer. NonSendableClass orsakar ett kompileringsfel vid försök att överföra via await eller till Task.
Sendable-typer i Swift delas in i tre kategorier: värdetyper med Sendable-egenskaper, slutgiltiga klasser med oföränderligt tillstånd och funktioner/slutningar markerade med @Sendable. Kompilatorn härleder automatiskt överensstämmelse med Sendable för värdetyper.
Struct, enum och tuple blir automatiskt Sendable om alla deras egenskaper och associerade värden också är Sendable. Detta är ett konservativt tillvägagångssätt: om även en enda egenskap inte följer Sendable kommer hela typen inte att anses säker.
| Typ | Sendable | Villkor |
|---|---|---|
| Struct | Ja (utan explicit angivelse) | Alla egenskaper Sendable |
| Enum | Ja (utan explicit angivelse) | Alla associerade värden Sendable |
| Final class | Ja (med explicit angivelse) | Alla let-egenskaper Sendable, inga var |
| Non-final class | Nej | Kan inte vara Sendable på grund av arv |
Alla inbyggda Swift-typer — Int, String, Double, Bool, Optional, Array, Dictionary, Set — följer Sendable. Detta gör typsammansättning som standard säker. Utvecklaren behöver bara vara uppmärksam på anpassade klasser.
@unchecked Sendable — är en mekanism som gör att en klass explicit kan deklarera sig som Sendable och kringgå kompilatorns kontroller. Utvecklaren tar ansvar för trådsäkerheten för en sådan klass. Detta är användbart för Objective-C-bryggor och optimerade strukturer.
@unchecked Sendable tillämpas när en klass internt garanterar säkerhet genom lås eller atomära operationer, men kompilatorn inte kan kontrollera detta statiskt. Till exempel en klass med os_unfair_lock eller pthread_mutex_t — dess trådsäkerhet säkerställs av koden, men Swift ser inte detta.
final class AtomicCounter: @unchecked Sendable {
private var value: Int = 0
private let lock = NSLock()
func increment() {
lock.lock()
value += 1
lock.unlock()
}
}
Använd @unchecked Sendable försiktigt. Detta är en tydlig signal till andra utvecklare: ”Jag har kontrollerat, det är säkert”. Ett fel i implementeringen kan leda till svårupptäckta race-förhållanden. Innan du tillämpar @unchecked, se till att typen verkligen inte kan skrivas om som en värdetyp.
Actor och Sendable är två sidor av samma mynt. Actor isolerar sitt tillstånd, men för datautbyte med omvärlden måste den returnera Sendable-typer. Om en actormetod returnerar en icke-Sendable-typ, ger kompilatorn en varning eller ett fel.
När en actor skickar data till extern kod passerar dessa data isoleringsgränsen. Sendable garanterar att mottagaren kan använda dem säkert utanför aktorn. Själva aktorn förblir isolerad — dess interna tillstånd avslöjas inte.
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 — är en Sendable-struktur som endast innehåller let-egenskaper av Sendable-typer. Detta tillvägagångssätt är best practice för att extrahera data från en actor. En ögonblicksbild av tillståndet överförs efter värde och aktorn förlorar inte kontrollen över sitt tillstånd.
@Sendable — är ett attribut för funktioner och slutningar som garanterar att slutningen inte fångar icke-Sendable-data på ett muterbart sätt. När en slutning skickas till Task eller en actormetod måste den vara Sendable.
Kompilatorn kontrollerar att @Sendable-slutningen inte fångar muterbara referenser till klasser. Infångning av let-egenskaper av Sendable-typer är tillåtet. Infångning av en var-variabel av referenstyp orsakar ett fel, eftersom slutningen kan köras samtidigt med mutation.
func performAsync(operation: @Sendable () -> Void) {
Task {
await operation()
}
}
let constant = "Safe"
var counter = 0
// counter — att fånga var muterar räknaren, kompileringsfel
performAsync { // ❌ Mutation av fångad var
print(constant)
}
Regeln är enkel: en @Sendable-slutning kan bara fånga sådan data som själv är Sendable och inte kommer att muteras utifrån. För klasser är infångning av weak self-referens tillåten om klassen inte är markerad som Sendable. Detta förhindrar klassiska retain-cykler och race-förhållanden.
Vanliga frågor
Sendable — är en markör som säger till kompilatorn: ”Den här typen kan säkert överföras mellan trådar”. Strukturer och konstanter är vanligtvis Sendable som standard, klasser — inte, om det inte anges explicit.
Värdetyper (struct, enum) med Sendable-egenskaper, slutgiltiga klasser med oföränderligt tillstånd, samt alla grundläggande Swift-typer: Int, String, Double, Bool, Array, Dictionary, Optional — följer automatiskt Sendable.
@unchecked Sendable — är ett sätt att deklarera en klass som Sendable utan statisk kompilatorkontroll. Utvecklaren garanterar säkerheten själv, ofta genom lås eller atomära operationer.
Actor isolerar sitt tillstånd, men när data returneras till extern kod passerar dessa data isoleringsgränsen. Sendable garanterar att mottagaren inte stöter på race-förhållanden vid användning av dessa data utanför aktorn.
Lägg till protokollet Sendable till en slutgiltig klass vars alla egenskaper är konstanter av Sendable-typer. Om klassen använder lås kan @unchecked Sendable tillämpas, men detta kräver försiktighet.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också