【问题标题】:Retain cycle when grabing values or keys from Dictionary in Swift在 Swift 中从 Dictionary 中获取值或键时保持循环
【发布时间】:2014-07-11 14:31:54
【问题描述】:

当我从字典中获取值并将它们放入数组时,我无法再释放内存。我试图从 Array 和 Dictionary 中删除所有对象,但这些对象仍然存在于某处(未调用 deinit)。

我是这样玩的:

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???

如果我删除这两行进行值提取,则正常调用 Deinit

var anotherDictionary:Dictionary<String, MyData> = ["key1":MyData(), "key2":MyData()]
anotherDictionary = [:] 
// Deinit is called twice

var myArray:MyData[] = [MyData(), MyData()]
myArray = []  
// Deinit is called twice

我在这里做错了什么?

当不再需要对象时,应如何以适当的方式删除对象以释放内存?仅当从 Dictionary(Dictionary.values 或 Dictionary.keys)中提取键或值时才会出现此问题。

编辑:

我为这种情况做了一个解决方法。如果我使用 NSDictionary 而不是 Dictionary 并首先提取键,然后在 for 循环中取值,那么它就可以工作。

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...

但是,如果我使用 allValues 而不是 allKeys,那么它将不再起作用。

...
var anotherValues = myDictionary.allValues
anotherValues = []
myDictionary = [:]
// deinit is not called again - very scary...

【问题讨论】:

  • 有趣的实验。我想知道是否每次克隆集合时都会发生这种情况,或者它是否特定于字典键。我的猜测是前者。
  • 我认为这是一个缺陷。即使你把它放在一个自动释放池中,它也不会调用 deinit。
  • 这是在游乐场吗?
  • @Matt - 不,它在一个包含在一个函数中的项目中。我也在操场上进行了测试,但即使在正常情况下(最后两行代码)也不会调用 deinit。
  • 很好;作为调试显示的一部分,playgrounds 倾向于挂在引用上,所以我只是确保这不会混淆问题。

标签: arrays dictionary swift retain-cycle


【解决方案1】:

我不认为这是一个保留周期。我可以简单地通过迭代字典中的值来重现这一点,甚至不引入数组,并且不对它们做任何事情。当我注释掉一些代码并发现这仍然没有调用 deinit 时,我正在处理这个问题:

    var myDictionary:Dictionary<String, MyData> = ["key1":MyData(), "key2":MyData()]
    for (_, item) in myDictionary {
        //myValues.append(item)
    }
    myDictionary = [:]
    // Why Deinit was not called???

如果您完全取出 for 循环,则会按预期调用 deinit。如果你把它放回去——只是循环,不做任何事情——那么你就不会得到一个 deinit。

我会说这是某种字典访问中的错误;它不像数组和字典之间的引用循环那么复杂。

我可以用这个作为我的for 循环来重现上述内容:

for _ in myDictionary { }

...如果我把那条线去掉,那就没问题了。这是我能找到的最简单/最奇怪的情况。

因此,您示例中的数组是一个红鲱鱼,我认为您发现了字典访问的错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-19
    • 1970-01-01
    • 2018-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多