Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое GlobalActor в Swift
GlobalActor — это новый тип актора, представленный в Swift 5.5 вместе с фреймворком Swift Concurrency. В отличие от обычных (неназначенных) акторов, которые изолируют своё состояние через экземпляры, GlobalActor — это синглтон-актор, который обеспечивает изоляцию данных на глобальном уровне, обычно на уровне всего приложения или критического домена. Основная цель — предоставить потокобезопасный доступ к общим ресурсам из разных частей программы, используя механизм акторов, но без необходимости создавать экземпляры.
Ключевое отличие от обычного актора
Обычный актор изолирует свои свойства и методы для каждого экземпляра:
actor Counter {
private var value = 0
func increment() { value += 1 }
}
let counter = Counter() // изоляция для этого экземпляра
GlobalActor же существует в единственном экземпляре (синглтон) и управляет доступом к ресурсам, которые должны быть глобально синхронизированы:
@globalActor
actor MyGlobalActor {
static let shared = MyGlobalActor() // синглтон-экземпляр
}
Зачем нужен GlobalActor?
- Изоляция глобального состояния: Защита общих ресурсов, таких как кэши, базы данных или сетевые менеджеры, которые используются во всём приложении.
- Обеспечение thread-safety: Гарантия, что код, исполняемый под этим актором, не будет вызывать data races.
- Упрощение миграции: Позволяет постепенно переносить legacy-код (например, блоки GCD) на модель акторов, аннотируя целые классы или методы.
- Контроль над выполнением: Все задачи, отправленные в глобальный актор, выполняются последовательно на его изолированном экземпляре (
shared).
Как использовать GlobalActor
Пример объявления и применения:
import Swift
// 1. Объявляем глобальный актор
@globalActor
struct ImageCacheActor {
// 2. Обязательно реализуем синглтон shared
actor ActorType { }
static let shared: ActorType = ActorType()
}
// 3. Аннотируем глобальное состояние или функции
@ImageCacheActor
class ImageCache {
private var cache: [String: UIImage] = [:]
func getImage(for key: String) -> UIImage? {
return cache[key]
}
func setImage(_ image: UIImage, for key: String) {
cache[key] = image
}
}
// 4. Использование: вызов методов будет автоматически изолирован
Task {
let cache = ImageCache()
await cache.setImage(UIImage(), for: "avatar")
let image = await cache.getImage(for: "avatar")
}
Важные аспекты
- Статическое свойство
shared: Должно возвращать экземпляр актора, который будет использоваться для синхронизации. Обычно это вложенныйactor(как в примере выше) или сам глобальный актор, если он объявлен какactor. - Аннотации:
@GlobalActorNameприменяется к классам, свойствам, функциям или замыканиям. Это указывает компилятору, что доступ должен быть изолирован черезshared-актор. - Взаимодействие с
async/await: Как и с обычными акторами, вызовы к изолированным сущностям требуютawait, так как компилятор гарантирует, что выполнение происходит в контексте актора. - Отличие от
@MainActor:@MainActor— это предопределённый GlobalActor в Swift, который изолирует код на главной очереди. По сути, это встроенная реализация глобального актора для работы с UI.
Практический пример
Допустим, у нас есть логгер, который пишет в файл из разных потоков:
@globalActor
actor FileLoggerActor {
static let shared = FileLoggerActor()
private var fileHandle: FileHandle?
func log(_ message: String) {
// Запись в файл, защищённая от конкурентного доступа
}
}
@FileLoggerActor
class Logger {
static let shared = Logger()
private init() {}
func logEvent(_ event: String) {
// Этот метод автоматически изолирован FileLoggerActor.shared
print("Log: \(event)")
}
}
// Использование из любой точки программы
Task.detached {
await Logger.shared.logEvent("App started") // Безопасный доступ
}
Вывод
GlobalActor — это мощный инструмент в арсенале Swift Concurrency для управления глобальным состоянием. Он сочетает преимущества акторов (автоматическая синхронизация, отсутствие data races) с удобством синглтона. Однако важно использовать его обдуманно: чрезмерное применение может привести к serialize bottlenecks, если множество задач будет блокировано на одном акторе. В целом, это отличное решение для изоляции разделяемых ресурсов в многопоточных iOS-приложениях.