当一个弱引用的对象(任意types)被释放时,我可以挂钩吗?

我正在编写一个Swift中的容器类,它的工作方式与Java java.util.WeakHashMap类似。 我目前的实施是在这里。

 class WeakRefMap<Key: Hashable, Value: AnyObject> { private var mapping = [Key: WeakBox<Value>]() subscript(key: Key) -> Value? { get { return mapping[key]?.raw } set { if let o = newValue { mapping[key] = WeakBox(o) } else { mapping.removeValueForKey(key) } } } var count: Int { return mapping.count } } class WeakBox<E: AnyObject> { weak var raw: E! init( _ raw: E) { self.raw = raw } } 

在这个实现中,容器中的WeakBox对象通过WeakBox被弱引用,所以保存值永远不会阻止不再需要的对象被释放。

但是这个代码显然是有问题的。 即使在其input对象被释放之后,条目仍然保留。

为了解决这个问题,我需要在释放一个holded对象之前挂钩,并删除它(相应的)条目。 我知道只有NSObject的解决scheme,但它不适用于AnyObject

任何人都可以帮我吗? 谢谢。 (^_^)

可悲的是,当weak var raw属性值被释放时, didSet或者willSet观察者不会被调用。

所以,在这种情况下你必须使用objc_setAssociatedObject

 // helper class to notify deallocation class DeallocWatcher { let notify:()->Void init(_ notify:()->Void) { self.notify = notify } deinit { notify() } } class WeakRefMap<Key: Hashable, Value: AnyObject> { private var mapping = [Key: WeakBox<Value>]() subscript(key: Key) -> Value? { get { return mapping[key]?.raw } set { if let o = newValue { // Add helper to associated objects. // When `o` is deallocated, `watcher` is also deallocated. // So, `watcher.deinit()` will get called. let watcher = DeallocWatcher { [unowned self] in self.mapping[key] = nil } objc_setAssociatedObject(o, unsafeAddressOf(self), watcher, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC)) mapping[key] = WeakBox(o) } else { mapping[key] = nil } } } var count: Int { return mapping.count } deinit { // cleanup for e in self.mapping.values { objc_setAssociatedObject(e.raw, unsafeAddressOf(self), nil, 0) } } } 

注意:在Swift 1.2之前。 这个解决scheme不适用于任意的Swift类。

以前的例子有一些错误,例如:

字典错误的大小无效:此示例打印“1”而不是“2”:

 let dict = WeakRefMap<String, NSObject>() autoreleasepool { let val = NSObject() dict["1"] = val dict["2"] = val print("dict size: \(dict.count)") } 

修正WeakRefMap:

 private class DeallocWatcher<Key: Hashable> { let notify:(keys: Set<Key>)->Void private var keys = Set<Key>() func insertKey(key: Key) { keys.insert(key) } init(_ notify:(keys: Set<Key>)->Void) { self.notify = notify } deinit { notify(keys: keys) } } public class WeakRefMap<Key: Hashable, Value: AnyObject> { private var mapping = [Key: WeakBox<Value>]() public init() {} public subscript(key: Key) -> Value? { get { return mapping[key]?.raw } set { if let o = newValue { // Add helper to associated objects. // When `o` is deallocated, `watcher` is also deallocated. // So, `watcher.deinit()` will get called. if let watcher = objc_getAssociatedObject(o, unsafeAddressOf(self)) as? DeallocWatcher<Key> { watcher.insertKey(key) } else { let watcher = DeallocWatcher { [unowned self] (keys: Set<Key>) -> Void in for key in keys { self.mapping[key] = nil } } watcher.insertKey(key) objc_setAssociatedObject(o, unsafeAddressOf(self), watcher, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } mapping[key] = WeakBox(o) } else { if let index = mapping.indexForKey(key) { let (_, value) = mapping[index] objc_setAssociatedObject(value.raw, unsafeAddressOf(self), nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) mapping.removeAtIndex(index) } } } } public var count: Int { return mapping.count } deinit { // cleanup for e in self.mapping.values { objc_setAssociatedObject(e.raw, unsafeAddressOf(self), nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } }