我试图弄清楚NSMapTable是如何工作的所以我在操场上尝试以下代码:
class Person {
var name: String
init(name: String ) {
self.name = name
print("(name) is being initialized")
}
deinit {
print("(name) is being deinitialized")
}
}
var hobyePerson : NSMapTable? = NSMapTable<Person, NSMutableString>
(keyOptions: .weakMemory, valueOptions: .weakMemory)
var rob : Person? = Person(name: "Rob Appleseed") // print : Rob Appleseed is being initialized
hobyePerson?.setObject("golf", forKey: rob)
hobyePerson?.count // return : 1
rob = nil // print : Rob Appleseed is being deinitialized
hobyePerson?.count // return : 1 (WHY ???!!!!)
如文档中所述:"键和/或值可以选择"弱"保存,以便在回收其中一个对象时删除条目。
为什么即使我初始化了对象,以便在解除分配 rob 时它对键值对的弱引用,我在 hobyePerson 中仍然有一个元素?
当您不关心何时释放键/值时,NSMapTable
的weak
行为选项效果最好,而是关心键/值没有被强保留,并且会在感兴趣的对象变得nil
后的某个时间点释放。
为什么会这样?
作为基础课程,NSMapTable
的作者必须平衡功能和性能。
因此,作为性能的"优化",他们选择不会立即从地图表中删除变得nil
的弱引用对象......!相反,当可以有效地完成时,这会在"以后"发生 - 例如当地图表内部调整大小时,等等。
正如@Luke在他的回答中也提到的那样,请参阅这篇关于NSMapTable
行为实验的优秀文章以获取更多详细信息:
http://cocoamine.net/blog/2013/12/13/nsmaptable-and-zeroing-weak-references/
是的,这是一种奇怪而不幸的行为。本文对此进行了深入探讨。虽然它没有具体探讨弱到弱,但所描述的行为是相同的。正如该作者所指出的,hobyePerson.keyEnumerator().allObjects.count
和 hobyePerson.objectEnumerator().allObjects.count
将包含 0,正如所有这些结束时所预期的那样。他还指出,苹果已经在Mountain Lion发行说明中记录了这种行为。
但是,目前不建议使用弱到强的 NSMapTables,因为 弱键的强值不会得到 清除(并释放),直到/除非地图表调整自身大小。
对不起,我没有更好的解释给你。
它对我不起作用,所以我实现了像这样的简单弱地图。会随着时间的推移改进它,但现在工作:
import Foundation
private struct WeakValue<Value:AnyObject> {
weak var value: Value?
}
public class CSWeakValueDictionary<Key:AnyObject, Value:AnyObject> {
private let dictionary = NSMutableDictionary()
public subscript(source: Key) -> Value? {
get {
let value = (dictionary["(source)"] as? WeakValue<Value>)?.value
if value == nil { dictionary.removeObject(forKey: "(source)") }
return value
}
set { dictionary["(source)"] = WeakValue(value: newValue) }
}
}