Sendable — какво е това, протокол за безопасност на нишките

Автор: IT Sectr Публикувано: 2026-06-18 Време за четене: 10 мин

Sendable — е Swift протокол, който маркира типове, безопасни за пренасяне между нишки. При работа с конкурентност (actor, async/await, Task) Swift компилаторът изисква всички данни, пренасяни между изолирани контекстове, да съвпадат с Sendable. Това изключва случайното пренасяне на небезопасни типове, които могат да доведат до гонки на данни. Протоколът действа като договор: тип, които отговаря на Sendable, гарантира липсата на вътрешно несинхронизирано състояние. Според WWDC 2021, протоколът Sendable е задължителен елемент при проектирането на безопасна многонишкова архитектура.

Основни

  • Sendable — протокол, гарантиращ липсата на гонки на данни при пренасяне между нишки
  • Стойностни типове (struct, enum) автоматично отговарят на Sendable, ако всички тяхни свойства са Sendable
  • @unchecked Sendable — байпас механизъм за класове, чиято безопасност разработчикът поема на себе си
  • Sendable затваряния се маркират с @Sendable атрибут, който проверява улавянето на променливи
  • Компилаторът дава грешка, ако не-Sendable тип се пренася между изолирани контекстове

Какво е Sendable протоколът в Swift?

Sendable — е протокол от стандартната библиотека на Swift (SE-0302), който маркира типове, безопасни за пренасяне между домейни на изолация. Под домейн на изолация се разбира actor, Task задача или @MainActor контекст. Sendable не съдържа изисквания — действа като маркер за безопасност.

Защо е нужен Sendable

Преди Swift 5.5 разработчиците могаха да пренасят всеки обект между нишки чрез DispatchQueue, и компилаторът не проверяваше дали е безопасно. Sendable затваря този пробив: сега компилаторът сам проследя междунишковите пренасяния и блокира небезопасните. Това прави конкурентния код по-надежден още преди изпълняването му.

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

class NonSendableClass {
    var counter: Int = 0
}

В примера UserProfile може да бъде безопасно пренасен между нишки, тъй като е структура с константни свойства на Sendable типове. NonSendableClass ще причини грешка при компилация при опит за пренасяне чрез await или в Task.

Какви типове по подразбиране отговарят на Sendable

Sendable типове в Swift се разделят на три категории: стойностни типове с Sendable свойства, финални класове с непроменливо състояние и функции/затваряния, маркирани с @Sendable. Компилаторът автоматично извежда съответствието с Sendable за стойностните типове.

Стойностни типове и автоматично съответствие

Struct, enum и tuple автоматично стават Sendable, ако всички тяхни свойства и асоциирани стойности също са Sendable. Това е консервативен подход: ако дори едно свойство не отговаря на Sendable, целият тип няма да бъде считан за безопасен.

ТипSendableУсловие
StructДа (без изрично посочване)Всички свойства Sendable
EnumДа (без изрично посочване)Всички асоциирани стойности Sendable
Final classДа (с изрично посочване)Всички let свойства Sendable, без var
Non-final classНеНе може да бъде Sendable поради наследяване

Базови типове

Всички вградени Swift типове — Int, String, Double, Bool, Optional, Array, Dictionary, Set — отговарят на Sendable. Това прави композицията на типове по подразбиране безопасна. Разработчикът трябва да наблюдава само за потребителски класове.

Sendable и класове: @unchecked Sendable

@unchecked Sendable — е механизъм, който позволява на един клас явно да се обяви като Sendable, заобиколявайки проверките на компилатора. Разработчикът поема отговорността за нишковата безопасност на такъв клас. Полезно е за Objective-C мостове и оптимизирани структури.

Кога да използваме @unchecked Sendable

@unchecked Sendable се прилага, когато един клас вътрешно гарантира безопасност чрез заключването на блокиовки или атомни операции, но компилаторът не може да ги провери статично. Например, клас с os_unfair_lock или pthread_mutex_t — нишковата му безопасност се осигурява от кода, но Swift не вижда това.

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

Използвайте @unchecked Sendable внимателно. Това е ясен сигнал за други разработчици: „Проверих го, безопасно е”. Грешка в имплементацията може да доведе до трудно за откриване гонки. Преди да приложите @unchecked, убедете се, че типът наистина не може да бъде преписан като стойностен тип.

Sendable в комбинация с Actor

Actor и Sendable са две страни на една и съща монета. Actor изолира своето състояние, но за обмяна на данни с външния свят трябва да върща Sendable типове. Ако метод на actor върща не-Sendable тип, компилаторът дава предупреждение или грешка.

Пренасяне на данни чрез Sendable

Когато actor изпраща данни на външен код, тези данни пресичат границата на изолация. Sendable гарантира, че получателят може да ги използва безопасно ивн извн actor-а. Самият actor остава изолиран — вътрешното му състояние не се разкрива.

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 — е Sendable структура, която съдържа само let свойства на Sendable типове. Този подход е best practice за извличане на данни от actor. Снимката на състоянието се пренася по стойност, и actor-ът не губи контрол над своето състояние.

Sendable функции и затваряния

@Sendable — е атрибут за функции и затваряния, който гарантира, че затварянето не улавя не-Sendable данни по променлив начин. Когато затварянето се пренася на Task или метод на actor, то трябва да бъде Sendable.

Проверка на улавянето в @Sendable затваряния

Компилаторът проверява, че @Sendable затварянето не улавя променливи референции към класове. Улавянето на let свойства на Sendable типове е разрешено. Улавянето на var променлива на референтен тип ще причини грешка, тъй като затварянето може да се изпълни едновременно с мутацията.

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

let constant = "Safe"
var counter = 0
// counter — улавянето на var мутира брояча, грешка при компилация
performAsync { // ❌ Мутация на уловен var
    print(constant)
}

Правилото е просто: @Sendable затварянето може да улавя само онези данни, които сами са Sendable и няма да бъдат мутирани отвън. За класовете е разрешено улавянето на слабата референция weak self, ако класът не е маркиран като Sendable. Това предотвратява класическите retain cycle и race condition.

Често задавани въпроси

Какво е Sendable в Swift с прости думи?

Sendable — е маркер, който казва на компилатора: „Този тип може безопасно да се пренася между нишки”. Структурите и константите обикновено са Sendable по подразбиране, класовете — не, освен ако е изрично посочено.

Какви типове автоматично отговарят на Sendable?

Стойностните типове (struct, enum) с Sendable свойства, финалните класове с непроменливо състояние, а също всички базови Swift типове: Int, String, Double, Bool, Array, Dictionary, Optional — автоматично отговарят на Sendable.

Какво е @unchecked Sendable?

@unchecked Sendable — е начин да се обяви един клас като Sendable без статична проверка от компилатора. Разработчикът самостоятелно гарантира безопасността, често чрез заключването на блокиовки или атомни операции.

Защо Sendable е нужен за Actor?

Actor-ът изолира своето състояние, но при върщането на данни на външен код тези данни пресичат границата на изолация. Sendable гарантира, че получателят няма да се срещне с гонки при използването на тези данни ивн извн actor-а.

Как да направим клас Sendable в Swift?

Добавете протокол Sendable към финален клас, чиито всички свойства са константи на Sendable типове. Ако класът използва заключването на блокиовки, може да се приложи @unchecked Sendable, но това изисква внимание.

Резюме

  • Sendable — маркер протокол за безопасно пренасяне на данни между нишки
  • Стойностните типове автоматично отговарят на Sendable при изпълнени условията
  • Класовете изискват явно посочване на Sendable или @unchecked Sendable
  • Actor върща данни на външен код само чрез Sendable типове
  • @Sendable атрибут на затварянията проверява правилността на улавянето на променливи
  • Компилаторът статично проверява съответствието с Sendable, предотвратявайки гонките на етапа на изграждане

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също