Sendable — egy Swift protokoll, amely megjelöli a szálak közötti átvitelre biztonságos típusokat. A konkurenciával (actor, async/await, Task) végzett munka során a Swift fordító megköveteli, hogy az izolált kontextusok között átvitt összes adat megfeleljen a Sendable-nek. Ez kizárja a nem biztonságos típusok véletlen átvitelét, amelyek adatversenyhez vezethetnek. A protokoll szerződésként működik: a Sendable-nek megfelelő típus garantálja a belső nem szinkronizált állapot hiányát. A WWDC 2021 szerint a Sendable protokoll kötelező elem a biztonságos többszálú architektúra tervezésében.
Főbb pontok
Sendable — egy protokoll a Swift szabvány könyvtárból (SE-0302), amely megjelöli a biztonságos típusokat az izolációs tartományok közötti átvitelre. Izolációs tartomány alatt actor, Task feladat vagy @MainActor kontextus értendő. A Sendable nem tartalmaz követelményeket — biztonsági jelölőként működik.
A Swift 5.5 előtt a fejlesztők bármilyen objektumot átvihettek a szálak között a DispatchQueue-n keresztül, és a fordító nem ellenőrizte, hogy ez biztonságos-e. Sendable bezárja ezt a rést: most a fordító maga követi nyomon a szálak közötti átvitelt és blokkolja a nem biztonságosakat. Ez megbízhatóbbá teszi a konkurens kódot még a futtatás előtt.
struct UserProfile: Sendable {
let name: String
let age: Int
}
class NonSendableClass {
var counter: Int = 0
}
A példában a UserProfile biztonságosan átvihető a szálak között, mert ez egy struktúra konstans Sendable típusú tulajdonságokkal. A NonSendableClass fordítási hibát okoz, ha await-en keresztül vagy Task-ban próbálják átadni.
Sendable típusok a Swift-ben három kategóriába oszthatók: értéktípusok Sendable tulajdonságokkal, végső osztályok megváltoztathatatlan állapottal és @Sendable-lel jelölt függvények/lezárások. A fordító automatikusan következteti a Sendable-nek való megfelelt az értéktípusok számára.
Struct, enum és tuple automatikusan Sendable válnak, ha összes tulajdonságuk és kapcsolódó értékük is Sendable. Ez egy konzervatív megközelítés: ha még egy tulajdonság sem felel meg a Sendable-nek, az egész típus nem minősül biztonságosnak.
| Típus | Sendable | Feltétel |
|---|---|---|
| Struct | Igen (kifejezett jelölés nélkül) | Minden tulajdonság Sendable |
| Enum | Igen (kifejezett jelölés nélkül) | Minden kapcsolódó érték Sendable |
| Final class | Igen (kifejezett jelöléssel) | Minden let tulajdonság Sendable, nincs var |
| Non-final class | Nem | Öröklődés miatt nem lehet Sendable |
Az összes beépített Swift típus — Int, String, Double, Bool, Optional, Array, Dictionary, Set — megfelel a Sendable-nek. Ez alapértelmezettem biztonságossá teszi a típus kompozíciót. A fejlesztőnek csak az egyedi osztályokra kell figyelnie.
@unchecked Sendable — egy mechanizmus, amely lehetővé teszi egy osztály számára, hogy kifejezetten Sendable-ként nyilvánítsa magát, megkerülve a fordító ellenőrzéseit. A fejlesztő vállalja a felelősséget az ilyen osztály szálbiztonságáért. Hasznos az Objective-C hidak és optimalizált struktúrák számára.
A @unchecked Sendable akkor alkalmazandó, amikor az osztály belsőleg garantálja a biztonságot zárakon vagy atomi műveleteken keresztül, de a fordító ezt statikusan nem tudja ellenőrizni. Például egy osztály os_unfair_lock vagy pthread_mutex_t -val — a szálbiztonságot a kód biztosítja, de a Swift nem látja ezt.
final class AtomicCounter: @unchecked Sendable {
private var value: Int = 0
private let lock = NSLock()
func increment() {
lock.lock()
value += 1
lock.unlock()
}
}
A @unchecked Sendable-t óvatosan használja. Ez egyértelmű jelzés más fejlesztők számára: „Ellenőriztem, biztonságos”. A megvalósításban előforduló hiba nehezen felderíthető versenyhelyzetekhez vezethet. A @unchecked alkalmazása előtt győződjön meg arról, hogy a típus tényleg nem írható át értéktípusként.
Actor és Sendable ugyanazon érem két oldala. Az Actor izolálja állapotát, de a külvilággal való adatcseréhez Sendable típusokat kell visszaadnia. Ha egy actor metódus nem Sendable típust ad vissza, a fordító figyelmeztetést vagy hibát ad.
Amikor az actor adatokat küld a külső kódnak, ezek az adatok átlépik az izolációs határt. Sendable garantálja, hogy a címzett biztonságosan használhatja őket az actoron kívül. Maga az actor izolált marad — belső állapota nem kerül felfedésre.
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 — egy Sendable struktúra, amely csak Sendable típusok let tulajdonságait tartalmazza. Ez a megközelítés a legjobb gyakorlat az adatok actor-ból történő kinyerésére. Az állapot pillanatképe érték szerint kerül átvitelre, és az actor nem veszíti el az irányítást állapota felett.
@Sendable — egy attribútum függvények és lezárások számára, amely garantálja, hogy a lezárás nem rögzít nem Sendable adatokat módosítható módon. Amikor egy lezárást Task-ba vagy actor metódusba küldenek, annak Sendable-nek kell lennie.
A fordító ellenőrzi, hogy a @Sendable lezárás nem rögzít módosítható hivatkozásokat osztályokra. A Sendable típusok let tulajdonságainak rögzítése megengedett. Egy referenciatípus var változójának rögzítése hibát okoz, mert a lezárás egyidejűleg hajtódhat végre a mutációval.
func performAsync(operation: @Sendable () -> Void) {
Task {
await operation()
}
}
let constant = "Safe"
var counter = 0
// counter — a var rögzítése módosítja a számlálót, fordítási hiba
performAsync { // ❌ A rögzített var mutációja
print(constant)
}
A szabály egyszerű: a @Sendable lezárás csak azokat az adatokat rögzítheti, amelyek maguk Sendable-ek és nem mutálódnak külről. Osztályok esetén a weak self gyenge hivatkozás rögzítése megengedett, ha az osztály nincs Sendable-ként jelölve. Ez megakadályozza a klasszikus retain cycle és race condition kialakulását.
Gyakran Ismételt Kérdések
Sendable — egy jelölő, amely azt mondja a fordítónak: „Ez a típus biztonságosan átvihető a szálak között”. A struktúrák és konstansek általában alapértelmezettem Sendable-ek, az osztályok — nem, hacsak nincs kifejezetten jelezve.
Értéktípusok (struct, enum) Sendable tulajdonságokkal, végső osztályok megváltoztathatatlan állapottal, valamint az összes alap Swift típus: Int, String, Double, Bool, Array, Dictionary, Optional — automatikusan megfelelnek a Sendable-nek.
@unchecked Sendable — egy módszer egy osztály Sendable-ként való deklarálására a fordító statikus ellenőrzése nélkül. A fejlesztő önállóan garantálja a biztonságot, gyakran zárakon vagy atomi műveleteken keresztül.
Az Actor izolálja állapotát, de az adatok külső kódnak történő visszaadásakor ezek az adatok átlépik az izolációs határt. Sendable garantálja, hogy a címzett nem ütközik versenyhelyzetekbe az adatok actoron kívüli használata során.
Adja hozzá a Sendable protokollt egy végső osztályhoz, amelynek összes tulajdonsága Sendable típusok konstansa. Ha az osztály zárakat használ, a @unchecked Sendable alkalmazható, de ez óvatosságot igényel.
Összefoglaló
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is