I'm trying to figure out how NSMapTable works So I'm trying in playground the following code:
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 ???!!!!)
as written in the documentation: "Keys and/or values are optionally held “weakly” such that entries are removed when one of the objects is reclaimed."
why even though I initialized the object so that it has a weak reference to the key-value pair when rob is deallocated, I still have an element in hobyePerson?
A collection similar to a dictionary, but with a broader range of available memory semantics. iOS 6.0+ iPadOS 6.0+ macOS 10.5+ Mac Catalyst 13.1+ tvOS 9.0+ watchOS 2.0+
An NSDictionary will retain it's objects, and copy it's keys. Here are some effects this has had on code I've worked on. Sometimes you get the same object you put in, sometimes not. Immutable objects are optimized to return themselves as a copy .
An object representing a static collection of key-value pairs, for use instead of a Dictionary constant in cases that require reference semantics.
NSMapTable
's weak
behavior options work best when you don't care when keys/values are released, but rather, you do care that the keys/values aren't strongly retained and will be released at some point after the object of interest becomes nil
.
Why so?
As a Foundation class, the authors of NSMapTable
had to balance both features and performance.
Consequently, as an "optimization" for performance, they chose that weakly referenced objects that become nil
are NOT immediately removed from the map table...! Rather, this happens "later" when it can be efficiently done -- such as when the map table internally gets resized, etc.
As @Luke also mentions in his answer, see this excellent writeup about an experiment done on NSMapTable
's behavior for more details:
http://cocoamine.net/blog/2013/12/13/nsmaptable-and-zeroing-weak-references/
Yes, this is a strange and unfortunate behavior. This article goes into it in some depth. Although it doesn't explore weak-to-weak specifically, the behavior described is the same. As that author notes, hobyePerson.keyEnumerator().allObjects.count
and hobyePerson.objectEnumerator().allObjects.count
will contain 0 as expected at the end of all this. He also points out that Apple has sort of documented this behavior in the Mountain Lion release notes.
However, weak-to-strong NSMapTables are not currently recommended, as the strong values for weak keys which get zero’d out do not get cleared away (and released) until/unless the map table resizes itself.
Sorry I don't have a better explanation for you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With