功能
保证成员变量的存取线程安全。
知识点
-
propertyWrapper
-
dynamicMemberLookup
-
defer
功能实现
简单的锁封装
extension NSLock {
func around<T>(_ closure: () throws -> T) rethrows -> T {
lock(); defer { unlock() }
return try closure()
}
func around(_ closure: () throws -> Void) rethrows {
lock(); defer { unlock() }
try closure()
}
func around(_ closure: () -> Void) {
lock(); defer { unlock() }
closure()
}
}
这样封装之后,可以屏蔽加锁的细节代码
lock()
someCode()
unLock()
变为
lock.around {
someCode()
}
成员变量的锁机制
Objc中可以使用atomic,但是Swift就比较麻烦
var _lockName = ""
var lockName: String {
get {
return lock.around {
self._lockName
}
}
set {
lock.around {
self._lockName = newValue
}
}
}
这样的代码复用性极差,又臭又长
利用propertyWrapper实现加锁
简单的存取加锁
@propertyWrapper
class Protected<T> {
var lock = NSLock()
var value: T
var wrappedValue: T {
get {
lock.around {
self.value
}
}
set {
lock.around {
value = newValue
}
}
}
init(wrappedValue: T) {
self.value = wrappedValue
}
}
这样我们就可以方便的保证成员变量的线程安全性了,如下
class Person {
@Protected
var name = Name()
}
存取上下文加锁
只实现针对变量的存取加锁,并不满足所有场景。
通过propertyWrapper的projectedValue可以实现存取的上下文加锁
@propertyWrapper
class Protected<T> {
var lock = NSLock()
var value: T
var projectValue: Protected<T> { self }
// ....
func read<U>(_ closure: (T) throws -> U) rethrows -> U {
try lock.around {
try closure(self.value)
}
}
}
使用,如下
let p = Person()
p.$name.read { name in
someCode()
}
这样就可以保证整个闭包针对这个成员变量的使用都是线程安全的。
利用dynamicMemberLookup来保证加锁对象的成员变量线程安全
比如
@Protected
var person: Person // 如果保证我再使用name 和 age的时候也是线程安全的
实现如下
@propertyWrapper
@dynamicMemberLookup
class Protected<T> {
// ....
subscript<Property>(dynamicMember keyPath: WritableKeyPath<T, Property>) -> Property {
get { lock.around { value[keyPath: keyPath] } }
set { lock.around { value[keyPath: keyPath] = newValue } }
}
subscript<Property>(dynamicMember keyPath: KeyPath<T, Property>) -> Property {
lock.around { value[keyPath: keyPath] }
}
}
利用dynamicMemberLookup 针对keypath的读写进行加锁.
// 这样不仅可以保证加锁对象是安全的,其对象的存取也是加锁的
/// lock()
/// person.name
/// unlock()
$person.name