这是该问题的后续内容。

在讨论有关我在此处发布的代码的一些详细信息时,我遇到了线程安全性问题。经过搜索和尝试不同的事情之后,我找到了一个潜在的解决方案,现在在这里提出。在我的测试过程中,此方法没有任何问题,尽管由于线程安全性并不容易,所以我想问:此实现是真正的线程安全性吗?

线程安全性问题如下:字典上的键可以在任何线程上释放。这意味着,当键消失时,也可以在任何线程上调用回调块。反过来,这意味着可以在访问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?