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?
CodePudding user response:
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)
copiesdicKey
into a new key object K₂; K₁ is left alonedicKey = "changedKey"
creates a new object K₃ with the value "changedKey", and assignsdicKey
to point to it- Since nothing points to K₁ anymore, its reference count goes to 0 and the object is deallocated
mutableDic.setObject("two", forKey: dicKey)
copiesdicKey
into a new key object K₄; K₂ is left alone
The 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 assignscacheKey
to point to it- Since the cache is still retaining K₁, it remains alive and in memory, even if
dicKey
no longer points to it
- Since the cache is still retaining K₁, it remains alive and in memory, even if
cache.setObject("two", forKey: cacheKey)
retains K₂ for insertion into the cache
The 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"