【发布时间】:2021-11-18 00:00:18
【问题描述】:
我遇到了我不理解的内存“泄漏”。我有一个缓存类,其目的是限制重型数据项的实例数量。即使它存储了这些项目的 NO 个实例,它们也会以某种方式被保留。如果它们来自后台线程,我相信它们会在任务完成后被释放。但是,当然,正如代码所示,它们在主线程上仍然存在。
这发生在 iPadOS 14.8、iPadOS 15 和 MacCatalyst 上。
我遗漏了什么或不明白什么?
class DataCache {
var id: String
var data: Data? {
get {
let url = FileManager.default.temporaryDirectory
.appendingPathComponent("\(id).txt")
return try! Data(contentsOf: url)
}
set {
let url = FileManager.default.temporaryDirectory
.appendingPathComponent("\(id).txt")
try! newValue!.write(to: url)
}
}
init(id: String, data: Data) {
self.id = id
self.data = data
}
}
class Item {
static var selection = [Item]()
static var items = [String:Item]()
var id: String = UUID().uuidString
var itemData: DataCache
init() {
itemData = DataCache(id: id,
data: Data(String(repeating: "dummy", count: 4_000_000).utf8)
)
}
required init(_ other: Item) {
self.itemData = DataCache(id: self.id, data: other.itemData.data!)
}
func duplicate(times: Int) {
for index in 0..<times {
print(index)
Item.selection.append(Item(self))
}
}
}
@main struct Main {
static func main() throws {
let item = Item()
perform(item)
performOnSelection() { item in
let _ = Item(item)
}
while (true) {}
}
static func perform(_ item: Item) {
item.duplicate(times: 100)
}
static func performOnSelection(perform action: @escaping (Item)->Void) {
var done = false
DispatchQueue.global().async {
for item in Item.selection {
action(item)
}
done = true
}
while !done { sleep (1) }
}
}
【问题讨论】:
-
您可以使用 Xcode 中的内存图表工具查看谁在保留对“泄露”对象的引用。
-
完全不相关,但是如果你只是阻塞调用线程直到
done设置为 true,那么调度到全局队列是没有意义的。 (顺便说一句,done的更新不是线程安全的。) -
这只是一个最小的、可重现的例子。它实际上来自一个不需要完成和等待的 SwiftUI 应用程序。在这里,我只是添加了它们,以便有时间检查结果。我离开了全局队列以显示这些项目正在被释放,所以并不是真正的泄漏。但感谢您的评论和快速回答。
标签: ios swift caching memory-leaks