Вы делаете мутацию статики потокобезопасной, как и любую другую переменную, синхронизируя свое взаимодействие с ней.Например, вы можете сделать ваше открытое свойство вычисляемым свойством, которое синхронизирует доступ к некоторому частному свойству.Например:
class MyClass {
private static let lock = NSLock()
private static var _name: String = "Hello"
static var name: String {
get { lock.withCriticalSection { _name } }
set { lock.withCriticalSection { _name = newValue } }
}
}
Где
extension NSLocking {
func withCriticalSection<T>(block: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try block()
}
}
Или вы можете использовать последовательную очередь GCD или устройство чтения-записи для синхронизации.Идея была бы такой же, хотя:это часто неадекватно.Как правило, потокобезопасность достигается на более высоком уровне абстракции.
Учтите:
let group = DispatchGroup()
DispatchQueue.global().async(group: group) {
for _ in 0 ..< 100_000 {
MyClass.name += "x"
}
}
DispatchQueue.global().async(group: group) {
for _ in 0 ..< 100_000 {
MyClass.name += "y"
}
}
group.notify(queue: .main) {
print(MyClass.name.count)
}
Вы могли бы подумать, что, поскольку у нас есть поточно-ориентированные средства доступа, все в порядке.Но это не так.Это не добавит 200 000 символов к name
.Вам нужно сделать что-то вроде:
class MyClass {
private static let lock = NSLock()
private static var _name: String = ""
static var name: String {
get { lock.withCriticalSection { _name } }
}
static func appendString(_ string: String) {
lock.withCriticalSection {
_name += string
}
}
}
И тогда сработает следующее:
let group = DispatchGroup()
DispatchQueue.global().async(group: group) {
for _ in 0 ..< 100_000 {
MyClass.appendString("x")
}
}
DispatchQueue.global().async(group: group) {
for _ in 0 ..< 100_000 {
MyClass.appendString("y")
}
}
group.notify(queue: .main) {
print(MyClass.name.count)
}
Другой классический пример - это когда у вас есть два свойства, которые связаны друг с другом, дляНапример, может быть firstName
и lastName
.Вы не можете просто сделать каждое из двух свойств поточно-ориентированным, но вам нужно выполнить одну задачу по обновлению обоих свойств как поточно-безопасных.
Это глупые примеры, но они демонстрируют, что иногда более высокий уровень абстракциинеобходим.Но для простых приложений может оказаться достаточной синхронизация методов доступа к вычисляемым свойствам.