Sendable — је Свифт протокол који означава типове безбедне за преношење између нити. При раду са конкурентношћу (actor, async/await, Task), Свифт компилатор захтева да сви подаци који се преносе између изолованих контекстабуду у складу са Sendable. Ово искључује случајно преношење небезбедних типова који могу довести до утрке података. Протокол делује као уговор: тип који испуњава Sendable гарантује одсуство унутрашњег несинхронизованог стања. Према WWDC 2021, Sendable протокол је обавезан елемент у пројектовању безбедне вишенитне архитектуре.
Главно
Sendable — је протокол из стандардне Swift библиотеке (SE-0302) који означава типове безбедне за преношење између домена изолације. Под доменом изолације подразумева се actor, Task задатак или @MainActor контекст. Sendable не садржи захтеве — он делује као маркер безбедности.
Пре Swift 5.5, програмери су могли да пренесу било који објекат између нити путем DispatchQueue, а компилатор није проверавао да ли је то безбедно. Sendable затвара ову рупу: сада компилатор сам прати међунитне преносе и блокира небезбедне. То чини конкурентни код поузданијим још пре неговог покретања.
struct UserProfile: Sendable {
let name: String
let age: Int
}
class NonSendableClass {
var counter: Int = 0
}
У примеру, UserProfile може бити безбедно пренет између нити, јер је то структура са константним својствима Sendable типова. NonSendableClass ће призвати грешку компилације при покушају преношења путем await или у Task.
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. То чини композицију типова подразумевано безбедном. Програмер треба да пази само на прилагођене класе.
@unchecked Sendable — је механизам који омогућава класи да се експлицитно изјасни као Sendable, заобилазећи провере компилатора. Програмер преузима одговорност за безбедност нити такве класе. Корисно је за Objective-C мостове и оптимизоване структуре.
@unchecked Sendable се примењује када класа унутрашње гарантује безбедност путем брава или атомских операција, али компилатор не може да то статички провери. На пример, класа са os_unfair_lock или pthread_mutex_t — њена безбедност нити је осигурана кодом, али 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, уверите се да се тип заиста не може преписати као вредносни тип.
Actor и Sendable су две стране исте медаље. Actor изолује своје стање, али за размену података са спољашњим светом мора да враћа Sendable типове. Ако метода actor-а враћа тип који није Sendable, компилатор издаје упозорење или грешку.
Када actor шаље податке ванском коду, ови подаци прелазе границу изолације. Sendable гарантује да прималац може да их безбедно користи ван actor-а. Сам actor остаје изолован — његово унутрашње стање се не открива.
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 у промењивом облику. Када се затворање преноси у Task или методу actor-а, мора бити Sendable.
Компилатор проверава да @Sendable затворање не хвата промењиве референце на класе. Хватање let својстава Sendable типова је дозвољено. Хватање var промењиве референце проузроковаће грешку, јер се затворање може извршавати истовремено са мутацијом.
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 — је маркер који говори компилатору: „овај тип се може безбедно преносити између нити”. Структуре и константе су обично Sendable подразумевано, класе — не, осим ако је то експлицитно наведено.
Вредносни типови (struct, enum) са Sendable својствима, финалне класе са непромењивим стањем, као и сви основни Swift типови: Int, String, Double, Bool, Array, Dictionary, Optional — аутоматски су усаглашени са Sendable.
@unchecked Sendable — је начин да се класа прогласи као Sendable без статичке провере компилатора. Програмер самостално гарантује безбедност, често путем брава или атомских операција.
Actor изолује своје стање, али при враћању података ванском коду ови подаци прелазе границу изолације. Sendable гарантује да се прималац неће суочити са утркама при коришћењу ових података ван actor-а.
Додајте протокол Sendable за финалну класу чија су сва својства константе Sendable типова. Ако класа користи браве, може се применити @unchecked Sendable, али то захтева опрезност.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође