Retain cycle when grabing values or keys from Dictionary in Swift

做~自己de王妃 提交于 2019-12-08 15:58:08

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!