Swift结构体中的内存泄漏问题 - 如何解决?

15
我正在使用Swift 2(Xcode 7 beta 3)开发一个应用程序,并尽可能使用值类型(结构体和枚举)。根据苹果关于内存管理的文档,使用值类型不应该导致保留循环,并且它应该可以正常工作。
但是今天我在事件处理代码中遇到了大量的内存泄漏。我追踪了问题并将其减少到以下最简示例。
假设有一个定义了单个属性“value”的协议Item:
protocol Item {

    var value: String { get }

}

然后我们创建一个具体的结构体,实现Item协议并添加一个额外的属性additionalValue。让我们称这个结构体为FooItem

struct FooItem<T>: Item {

    let value: String
    let additionalValue: T

    init(value: String, additionalValue: T) {
        self.value = value
        self.additionalValue = additionalValue
    }

}

第三个谜题的关键是另一个结构体,它包装了一个实现了Item协议的项目。它被称为ItemWrapper

struct ItemWrapper {

    let item: Item

    init(item: Item) {
        self.item = item
    }

}

如果使用内存泄漏配置在Instruments中进行分析,每当创建一个带有FooItemItemWrapper值时,就会出现内存泄漏。

let item = FooItem(value: "protocol value", additionalValue: "foo item value")  
let _ = ItemWrapper(item: item) 

Instruments截图1 Instruments截图2

这里有一个示例Xcode项目Instruments文件https://www.dropbox.com/s/z6ugxzxqggrv1xl/SwiftStructsMemoryLeak.zip?dl=0

整个代码示例可以在此Gist中查看:https://gist.github.com/lukaskubanek/4e3f7657864103d79e3a

这是错误报告:rdar://21375421

这是Swift编译器的错误还是我做错了什么?


编辑1:正如评论中建议的那样,我在Apple Dev Forum上重新发布了这个问题,以便从Swift社区和语言开发人员那里获得更多关注。由于WWDC 2015期间开发论坛的迁移,我不得不在新论坛上发布更新的问题。这是链接:https://forums.developer.apple.com/message/9643


编辑2:我在示例代码中最初发布的问题似乎已在Swift 2.0中得到解决。由于它没有解决我的应用程序中的问题,我对示例代码进行了另一种修改。现在FooItem的附加属性具有通用类型,并且FooItem带有类型注释,因此是通用类型。这是我在应用程序中使用它的方式,它仍然会导致内存泄漏,但这次是在初始化ItemWrapper时而不是访问属性时。


编辑3:完全更新了问题,以反映在Swift 2.0中持续存在的修改问题,并上传了新的示例Xcode项目。


这个例子实际上只包括那三个实体和调用它们的代码。请参阅此Gist https://gist.github.com/lukaskubanek/4e3f7657864103d79e3a 以查看整个AppDelegate。抱歉在问题中没有提到它。 - Lukáš Kubánek
我能够重现这个问题,但是不知道到底发生了什么鬼.. +1 - Mazyod
有趣的是,这只发生在包装器上,如果你在包装器周围添加更多的包装器,情况并不会变得更糟。这可能是一个bug。你可能想要在开发者论坛上提出来,看看那里的人是否认为这是一个bug。这非常有趣,所以它很可能会迅速引起注意。 - Ben Kane
@BenKane 感谢你的提示!我已经在Swift开发者论坛上重新发布了这个问题。让我们看看结果如何。 - Lukáš Kubánek
@LukasKubanek 很酷,我会在那里关注它。 - Ben Kane
显示剩余2条评论
2个回答

4
虽然我在开发者论坛和错误跟踪器上都没有收到苹果的回应,也没有在最新测试版的发布说明中找到与此问题相关的内容,但在Swift编译器中似乎已经解决了这个问题,具体表现为在 Xcode 7 beta 5 中。(也许在beta 4中也有效。我检查过的最后一个版本是beta 3。)
演示项目不再产生内存泄漏,我的应用程序也是如此。太棒了! enter image description here

3

好的,这里有一个解决方法,尽管我不知道它为什么有效。我发现如果你这样做:

    let theItem = itemWrapper.item
    let value = theItem.value

...而不是这样:

    let value = itemWrapper.item.value

它不会导致内存泄漏。


我也找到了这个解决方法。问题是这只适用于属性。在我的实际代码中,我还定义了协议中的方法,它们也会产生内存泄漏。因此,我不得不编写函数来包装所有具体类型(如FooItem等),这并不好玩。 - Lukáš Kubánek
是的。我猜这只是一个Swift编译器的错误,希望最终会得到修复,"解决方案"就是提交一个radar并等待苹果公司解决它。(有一个编译器指令可以让你打开"Swift优化",这将给你一个不同的构建。你可以尝试打开它看看是否可以解决问题。但要注意,这也会引入一些已知的问题,所以这可能是一个权衡。) - cc.
好的,我会等待今天的公告(WWDC),如果这个错误在新的Swift版本中没有被修复,我会提交一个radar。 - Lukáš Kubánek
一年后我遇到了同样的情况。这个解决方法解决了我的内存泄漏问题。问题是,我也不明白为什么这个解决方案有效。如果这是Swift中的一个bug,我认为它应该是非常普遍的,并且现在应该已经得到解决... - Rados

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接