问题
When I grab values from a Dictionary and put them into Array, I can't release memory any more. I tried to remove all object from Array and Dictionary, but these object still exist somewhere (deinit were not called).
I was playing in the following way:
class MyData {
let i = 0
init () {
NSLog("Init")
}
deinit {
NSLog("Deinit")
}
}
var myDictionary:Dictionary<String, MyData> = ["key1":MyData(), "key2":MyData()]
// Init was called twice
// here is the problem: extract values from Dictionary
var myValues = Array(myDictionary.values)
myValues = [] // nothing - ok, strong references are still in the dictionary
myDictionary = [:]
// Why Deinit was not called???
If I remove these two lines for value extraction, then Deinit is called normally
var anotherDictionary:Dictionary<String, MyData> = ["key1":MyData(), "key2":MyData()]
anotherDictionary = [:]
// Deinit is called twice
var myArray:MyData[] = [MyData(), MyData()]
myArray = []
// Deinit is called twice
What am I doing wrong here?
How the objects should be removed in the proper way to release memory when they don't needed anymore? The problem happens only when keys or values are extracted from Dictionary (Dictionary.values or Dictionary.keys).
EDIT:
I made a workaround for this case. If I use NSDictionary instead of Dictionary and extract keys first and then take values in a for-loop, then it works.
var myDictionary:NSDictionary = ["key1":MyData(), "key2":MyData()]
// called Init twice
var myKeys:String[] = myDictionary.allKeys as String[]
var myValues:MyData[] = []
for key in myKeys {
myValues.append(myDictionary[key] as MyData)
}
myKeys = []
myDictionary = [:]
myValues = []
// Deinit is now called twice, but I'm not sure about keys...
But, if I use allValues instead of allKeys, then it won't work any more.
...
var anotherValues = myDictionary.allValues
anotherValues = []
myDictionary = [:]
// deinit is not called again - very scary...
回答1:
I don't think this is a retain cycle. I can reproduce this simply by iterating the values in the Dictionary without even introducing the Array, and without doing anything to them. I was playing with this issue when I commented out some of the code and found that this still doesn't call deinit:
var myDictionary:Dictionary<String, MyData> = ["key1":MyData(), "key2":MyData()]
for (_, item) in myDictionary {
//myValues.append(item)
}
myDictionary = [:]
// Why Deinit was not called???
If you take out the for
loop entirely, deinit is called as expected. If you put it back in—just looping, not doing anything—then you don't get a deinit.
I'd say this is a bug in dictionary access of some kind; it's not as complicated as reference cycles between Array and Dictionary.
I can reproduce the above with this as my for
loop:
for _ in myDictionary { }
...and if I take that line out, it deinits fine. That's the simplest/oddest case I could find.
So, the Array in your example is a red herring, and I think you've found a bug with dictionary access.
来源:https://stackoverflow.com/questions/24700178/retain-cycle-when-grabing-values-or-keys-from-dictionary-in-swift