我可以在释放弱引用对象(任意类型)时挂钩吗?
Can I hook when a weakly-referenced object (of arbitrary type) is freed?
我正在 Swift 中编写一个容器 class,其工作方式与 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
弱引用,因此持有值永远不会阻止对象在不再需要时被释放。
但是这段代码显然有问题;即使在其条目的对象被释放后条目仍然存在。
为了解决这个问题,我需要在持有的对象被释放之前挂钩,并删除它的(相应的)条目。我只知道 NSObject
的解决方案,但不适用于 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 之前。此解决方案不适用于任意 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)
}
}
}
我正在 Swift 中编写一个容器 class,其工作方式与 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
弱引用,因此持有值永远不会阻止对象在不再需要时被释放。
但是这段代码显然有问题;即使在其条目的对象被释放后条目仍然存在。
为了解决这个问题,我需要在持有的对象被释放之前挂钩,并删除它的(相应的)条目。我只知道 NSObject
的解决方案,但不适用于 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 之前。此解决方案不适用于任意 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)
}
}
}