Protocolul Sendable și Marcatorul @Sendable: Siguranța Transferului Concurent în Swift (iOS & macOS)

Ghid complet pentru protocolul Sendable și adnotarea @Sendable în Swift 6: verificarea siguranței transferului de date între domenii de izolare și eliminarea definitivă a curselor critice de date (Data Races) la compilare.

Sintaxă Swift
final class Config: Sendable { let key: String } | @Sendable () async -> Void

Pentru a împiedica două fire paralele să citească și să modifice simultan aceeași zonă de memorie, compilatorul Swift 6 verifică cu strictețe dacă datele transmise dintr-un domeniu de izolare în altul (de exemplu între o funcție asincronă, un actor sau o sarcină Task) sunt sigure pentru partajare. Această garanție este exprimată prin protocolul marcator Sendable.

Ce Tipuri Sunt Sendable în Mod Implicit?

  • Value Types: Structurile și enumerările ale căror proprietăți conțin exclusiv alte tipuri Sendable (ex: Int, String, Double, Array<Sendable>).
  • Actori (actor): Toți actorii sunt Sendable deoarece își serializează și protejează intern accesul la starea mutabilă.
  • Clase Imutabile Finale: O clasă final ale cărei proprietăți sunt exclusiv constante (let) de tipuri Sendable și care nu moștenește alte clase.
Definirea Tipurilor Sendable și Utilizarea Închiderilor @Sendable
// Structură implicit Sendable (toate câmpurile sunt Value Types imutabile):
struct MesajChat: Sendable {
    let id: UUID
    let continut: String
    let trimisLa: Date
}

// Clasă marcată explicit ca Sendable (trebuie să fie final și să aibă doar proprietăți 'let'):
final class ServerSettings: Sendable {
    let host: String
    let port: Int
    
    init(host: String, port: Int) {
        self.host = host
        self.port = port
    }
}

// Închiderile funcționale transmise sarcinilor paralele trebuie adnotate cu @Sendable:
// O închidere @Sendable nu are voie să captureze variabile mutabile 'var' prin referință!
func executaSarcinaFundal(actiune: @escaping @Sendable () async -> Void) {
    Task.detached {
        await actiune()
    }
}

Sincronizare Manuală cu @unchecked Sendable

Dacă dețineți o clasă care gestionează intern starea mutabilă folosind o primitivă de blocare de nivel scăzut (cum ar fi os_unfair_lock sau noul OSAllocatedUnfairLock din Swift), puteți dezactiva verificarea compilatorului folosind conformarea explicită @unchecked Sendable:

import os

final class ThreadSafeCounter: @unchecked Sendable {
    private let lock = OSAllocatedUnfairLock()
    private var count: Int = 0

    func increment() {
        lock.withLock {
            count += 1
        }
    }

    func read() -> Int {
        lock.withLock { count }
    }
}