← Назад к вопросам
Как определить, в каких случаях необходимо придерживаться протокола Sendable?
1.8 Middle🔥 142 комментариев
#Многопоточность и асинхронность
Комментарии (2)
🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда необходимо придерживаться протокола Sendable?
Протокол Sendable — это ключевой механизм статической проверки безопасности данных в конкурентном коде Swift (начиная с Swift 5.5 и акторах). Он указывает компилятору, что тип безопасен для использования в разных конкурентных контекстах (например, между акторами, задачами Task). Основные случаи, когда необходимо придерживаться Sendable:
1. Передача данных между изолированными конкурентными доменами
- При передаче значений через
Task, особенно между разными акторами или изолированными функциями. - При использовании
@Sendable-замыканий, которые захватывают внешние значения.
// Пример: передача Sendable типа в Task
struct UserData: Sendable {
let id: String
let score: Int
}
Task {
let data = UserData(id: "user123", score: 100)
await process(data: data) // Без Sendable будет предупреждение компилятора
}
2. Использование в акторах (Actor)
- Когда актор публикует свойства или возвращает значения, которые могут использоваться вне актора.
- При реализации
@MainActorдля передачи данных в UI.
actor DataManager {
private var cache: [String: SendableData] = [:]
func getData(for key: String) -> SendableData? {
return cache[key]
}
}
// SendableData должен быть Sendable
struct SendableData: Sendable {
let content: String
}
3. Работа с глобальными переменными или статическими свойствами
- Доступ к неизменяемым (
let) глобальным переменным из разных задач требуетSendable.
// Глобальная конфигурация должна быть Sendable
let appConfig: AppConfiguration? = nil // AppConfiguration должен быть Sendable
struct AppConfiguration: Sendable {
let apiEndpoint: String
}
4. Хранение в коллекциях, которые передаются между задачами
- Например, массивы, словари или наборы, которые передаются через
Taskили акторы.
5. Типы, используемые в @Sendable-замыканиях
- Все значения, захватываемые
@Sendable-замыканием, должны бытьSendable.
func fetchData(completion: @Sendable (Result<Data, Error>) -> Void) {
Task {
let data = try await downloadData()
completion(.success(data)) // Data и Error должны быть Sendable
}
}
Когда Sendable НЕ требуется?
- Локальное использование: если тип используется только в одном изолированном контексте (например, внутри одного актора).
- Неизменяемые (immutable) типы с Sendable-полями: компилятор часто автоматически выводит
Sendableдля структур, содержащих толькоSendable-свойства. - Классы с акторами или изоляцией: если класс изолирован к актору (
@MainActor class), передача экземпляров внутри этого актора не требуетSendable.
Критические аспекты реализации
- Структуры (value types): обычно автоматически
Sendable, если все их свойстваSendable. - Классы (reference types): требуют ручного соответствия
Sendableтолько если они неизменяемые (immutable) и наследникиNSObjectили с внутренней синхронизацией. - Запрещены захваты изменяемых ссылок:
Sendable-замыкания не могут захватывать изменяемые (var) не-Sendable объекты.
// Опасный пример - НЕ Sendable
class NonSendableClass {
var data: String = ""
}
Task { [nonSendable = NonSendableClass()] in // Предупреждение компилятора!
print(nonSendable.data)
}
Практическое правило
Применяйте Sendable явно, когда компилятор выдаёт предупреждения о безопасности данных в конкурентном коде, или при проектировании типов, которые будут использоваться в многозадачной среде. Это особенно важно в современных приложениях с активным использованием async/await и акторов, где Sendable предотвращает гонки данных и неопределённое поведение.