在讨论有关我在此处发布的代码的一些详细信息时,我遇到了线程安全性问题。经过搜索和尝试不同的事情之后,我找到了一个潜在的解决方案,现在在这里提出。在我的测试过程中,此方法没有任何问题,尽管由于线程安全性并不容易,所以我想问:此实现是真正的线程安全性吗?
线程安全性问题如下:字典上的键可以在任何线程上释放。这意味着,当键消失时,也可以在任何线程上调用回调块。反过来,这意味着可以在访问
dict
var时对其进行更改。我的解决方案是使用
dispatch_sync
协调所有读/写,包括发生在DeallocWatcher
回调块内部的一次读/写。 /> public class WeakKeyDictionary<K: AnyObject, V where K: Hashable> {
private var dict = SynchronizedValue(value: Dictionary<HashableWeakBox<K>, V>())
public var block: (V)->() = { _ in }
public init() {}
public init(dictionary: Dictionary<K, V>) {
for (k, v) in dictionary {
setValue(v, forKey: k)
}
}
public subscript(key: K) -> V? {
get { return valueForKey(key) }
set { setValue(newValue, forKey: key) }
}
public func valueForKey(key: K) -> V? {
return dict.get { public class SynchronizedValue<T> {
public let serialQueue = dispatch_queue_create("SynchronizedValue serial queue", DISPATCH_QUEUE_SERIAL)
private var value: T
public init(value v: T) { value = v }
/// Should only return value types or thread-safe reference types
public func get<V>(/*@noescape*/ action: (T) -> V) -> V {
var v: V?
dispatch_sync(serialQueue) {
v = action(self.value)
}
return v!
}
public func access(/*@noescape*/ action: (inout T) -> ()) {
dispatch_sync(serialQueue) {
action(&self.value)
}
}
/// Should only be used for value types or for thread-safe reference types
public func get() -> T {
return get { q4312078q }
}
public func set(v: T) {
access { q4312078q = v; return }
}
}
[HashableWeakBox(key)] }
}
public func setValue(newValue: V?, forKey key: K) {
let hashableBox = HashableWeakBox(key)
if let value = newValue {
let watcher = DeallocWatcher { [weak self] in
if let me = self {
if let v = me.syncedRemoveValueForKey(hashableBox) {
me.block(v)
}
}
}
objc_setAssociatedObject(key, unsafeAddressOf(self), watcher, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
dict.access { q4312078q[hashableBox] = value; return }
}
else {
objc_setAssociatedObject(key, unsafeAddressOf(self), nil, 0)
}
}
public func removeValueForKey(key: K) -> V? {
objc_setAssociatedObject(key, unsafeAddressOf(self), nil, 0)
return syncedRemoveValueForKey(HashableWeakBox(key))
}
private func syncedRemoveValueForKey(key: HashableWeakBox<K>) -> V? {
var v: V?
dict.access { v = q4312078q.removeValueForKey(key); return }
return v
}
public var count: Int { return dict.get { q4312078q.count } }
public var isEmpty: Bool { return dict.get { q4312078q.isEmpty } }
public var keyValues: [(K, V)] {
return dict.get { dict in
let v = dict.keys
.filter { k in k.value != nil }
.map { k -> (K, V) in (k.value!, dict[k]!) }
return Array(v)
}
}
public var keys: [K] {
return keyValues.map { (k, v) in k }
}
public var values: [V] {
return keyValues.map { (k, v) in v }
}
deinit {
// Callback is not called when deallocing the helpers because in this case (inside deinit) 'self' is already nil
dict.access {
for box in q4312078q.keys {
objc_setAssociatedObject(box.value, unsafeAddressOf(self), nil, 0)
}
}
}
}
extension WeakKeyDictionary: Printable {
public var description: String {
let contents = dict.get { dict -> [String] in
let v = dict.keys
.filter { q4312078q.value != nil }
.map { "\(q4312078q.value!) : \(dict[q4312078q]) " }
return Array(v)
}
return "[ " + ", ".join(contents) + "]"
}
}
extension WeakKeyDictionary: SequenceType {
public func generate() -> IndexingGenerator<Array<(K, V)>> {
return keyValues.generate()
}
}
private class HashableWeakBox<T: AnyObject where T: Hashable>: Hashable {
weak var value: T?
let hashValueWhenNil: Int
init(_ v: T) {
value = v
hashValueWhenNil = v.hashValue
}
var hashValue: Int { return value?.hashValue ?? hashValueWhenNil }
}
private class DeallocWatcher {
let callback: ()->()
init(_ c: ()->()) { callback = c }
deinit { callback() }
}
这是
SynchronizedValue
类(基本上是GCD串行队列的包装器):q4312078q
注意:
在计算的变量
keyValues
上,需要此行.filter { k in k.value != nil }
,因为在此dict.get { ... }
块运行时,值可以变为nil,并且由于该块仍在运行,因此尚未清除该值(相应的DeallocWatcher
回调正坐在队列中等待轮到。)
评论
如果该代码有效,并且您希望对其进行改进,那么这里肯定是热门话题!@sᴉɔuɐɹɥԀ是的,这就是我的想法。谢谢!
已经三年了您是否想查看自己的代码@Alex?
已经五年了。您是否想查看自己的代码@Alex?