I studied the differences between NSMutableDictionary
and NSCache
.
One of them was that NSMutableDictionary
copy its keys and NSCache
just retain it as a strong reference.
In addition, I found that the reason why the key is copied from the NSMutableDictionary
is that if the key is only retained, a strange situation occurs when setting another value after changing the key value.
let mutableDic = NSMutableDictionary()
var dicKey: NSString = "key"
mutableDic.setObject("one", forKey: dicKey)
dicKey = "changedKey"
mutableDic.setObject("two", forKey: dicKey)
print(mutableDic.object(forKey: "key") ?? "") //"one"
print(mutableDic.object(forKey: "changedKey") ?? "") //"two"
But when I performed the same action using NSCache
I felt something strange. Because it worked fine even though it does not copy its keys!
let cache = NSCache<NSString, NSString>()
var cacheKey: NSString = "key"
cache.setObject("one", forKey: cacheKey)
cacheKey = "changedKey"
cache.setObject("two", forKey: cacheKey)
print(cache.object(forKey: "key") ?? "") //"one"
print(cache.object(forKey: "changedKey") ?? "") //"two"
So I wonder, after all what the different results will be from copying and retaining.
Is there any advantage to retain keys rather than copy except no need to implement the NScopying protocol as a key? or vice versa?
Can you explain with examples?
The key here (ha) is in the assignment to dicKey
/cacheKey
. Specifically, the assignments
dicKey = "changedKey"
cacheKey = "changedKey"
are not changing the value of the original dicKey
and cacheKey
instances, but creating new string objects and setting the local variables to point to these new objects.
In the dictionary case:
dicKey
points to an object K₁ whose value is "key"mutableDic.setObject("one", forKey: dicKey)
copies dicKey
into a new key object K₂; K₁ is left alonedicKey = "changedKey"
creates a new object K₃ with the value "changedKey", and assigns dicKey
to point to it
mutableDic.setObject("two", forKey: dicKey)
copies dicKey
into a new key object K₄; K₂ is left aloneThe end result is that the dictionary contains K₂ and K₄, while dicKey
points to K₃.
In the cache case:
dicKey
points to an object K₁ whose value is "key"cache.setObject("one", forKey: cacheKey)
retains K₁ for insertion into the cachecacheKey = "changedKey"
creates a new object K₂ with a value "changedKey" and assigns cacheKey
to point to it
dicKey
no longer points to itcache.setObject("two", forKey: cacheKey)
retains K₂ for insertion into the cacheThe end result is that the cache contains K₁ and K₂, and cacheKey
points to K₂.
If instead of being an NSString
, dicKey
and cacheKey
were NSMutableString
, whose value can be modified at runtime without creating a new object, you'd see different behavior in the cache case:
let mutableDic = NSMutableDictionary()
var dicKey: NSMutableString = "key" // K₁
mutableDic.setObject("one", forKey: dicKey) // K₂
dicKey.setString("changedKey") // still K₁
mutableDic.setObject("two", forKey: dicKey) // K₃
print(mutableDic.object(forKey: "key") ?? "") // "one"
print(mutableDic.object(forKey: "changedKey") ?? "") // "two"
// BUT:
let cache = NSCache<NSString, NSString>()
var cacheKey: NSMutableString = "key" // K₁
cache.setObject("one", forKey: cacheKey) // still K₁
cacheKey.setString("changedKey") // still K₁
cache.setObject("two", forKey: cacheKey) // still K₁!
print(cache.object(forKey: "key") ?? "") // "" !!!
print(cache.object(forKey: "changedKey") ?? "") // "two"