6. Actor isolation
Из прошлых статей можно сделать вывод, что Actor является потокобезопасным контейнером, который изолирует свой стейт и предоставляет доступ к нему только 1 задаче за одну единицу времени. Глобальный актор - делает тоже самое, только он шарит изоляцию между всеми всеми вызовами.
isolation¶
Синхронные методы, находящиеся внутри Актора изолированы им, обращение к таким методам происходит через await. Пример ниже:
actor LocalCache<T> {
private var value: T?
let lifetime: TimeInterval
var cacheValue: T? {
value
}
init(value: T, lifetime: TimeInterval) {
self.value = value
self.lifetime = lifetime
}
func setNewCacheValue(_ newValue: T?) {
self.value = newValue
}
func dropCacheValue() {
self.value = nil
}
}
let cache = LocalCache(value: 2, lifetime: 1)
Task {
await cache.setNewCacheValue(5)
await cache.dropCacheValue()
let lifeTime = await cache.lifetime // кажется изоляция здесь лишняя
}
В примере выше изоляция необходима для изменяемых пропертей. Но вот для получения времени жизни кеша(cache.lifetime) - изоляция кажется излишней. Потому что cache.lifetime - неизменяемое проперти. Оно не будет изменяться на протяжении жизни всего эктора. Но как убрать эту лишнюю изоляцию?
nonisolated¶
Снять изоляцию с неизменяемых переменных¶
Если в экторе нам не требуется изоляция для какого-нибудь метода/переменной, то мы можем написать ключевое слово nonisolated и тогда изоляцию будет снята с переменной/функции
actor LocalCache<T> {
....
nonisolated let lifetime: TimeInterval
...
}
let cache = LocalCache(value: 2, lifetime: 1)
Task {
let lifeTime = cache.lifetime // Теперь писать await не нужно
}
Протоколы¶
Если нам потребуется сделать наследование эктора от протокола, то нам также потребуется использование nonisolated
protocol AnyService {
func setNewValue(_ newValue: Int)
}
actor Service: AnyService {
// ЗДЕСЬ БУДЕТ ОШИБКА КОМПИЛЯЦИИ
func setNewValue(_ newValue: Int) {
}
}
Вариант 1¶
protocol AnyService {
func setNewValue(_ newValue: Int)
}
actor Service: AnyService {
/// Убрали изоляцию и метод стал синхронным - соответствует протоколу
nonisolated func setNewValue(_ newValue: Int) { ... }
}
Вариант 2¶
protocol AnyService {
func setNewValue(_ newValue: Int) async
}
actor Service: AnyService {
/// Убрали изоляцию и метод стал синхронным - соответствует протоколу
func setNewValue(_ newValue: Int) async { ... }
}
На начальном этапе для дебага и общего понимания хочется увидеть есть ли какая-нибудь изоляция у метода -- для этого можно использовать макрос #isolation