问题描述
我在我的 SwiftUI 应用程序中有这个 APIRepresentative 类作为单例 EnvironmentObject
。该类具有 @Published var
,它按 ID 保存所有 InsightData
结构。
即使在不依赖于 insightData
的视图中,重复调用 getInsightData
函数也会导致我的应用中的内存使用量每次增加大约 200Kb 左右。在我的应用的整个生命周期中,这将导致内存使用量激增至数 GB。
关键在于:当我删除 @Published
的 insightData
修饰符时,内存泄漏消失了。然后我可以随心所欲地调用我的函数,而不会增加内存使用量。知道为什么会这样吗?我非常想保留 @Published
属性。
import Foundation
import Combine
final class APIRepresentative: ObservableObject {
private static let baseURLString = "https://apptelemetry.io/api/v1/"
@Published var insightData: [UUID: InsightDataTransferObject] = [:]
// More published properties
// ...
}
extension APIRepresentative {
func getInsightData(for insight: Insight,in insightGroup: InsightGroup,in app: TelemetryApp,callback: ((Result<InsightDataTransferObject,TransferError>) -> ())? = nil) {
let timeWindowEndDate = timeWindowEnd ?? Date()
let timeWindowBeginDate = timeWindowBeginning ?? timeWindowEndDate.addingTimeInterval(-60 * 60 * 24 * 30)
let url = urlForPath("apps",app.id.uuidString,"insightgroups",insightGroup.id.uuidString,"insights",insight.id.uuidString,Formatter.iso8601noFS.string(from: timeWindowBeginDate),Formatter.iso8601noFS.string(from: timeWindowEndDate)
)
let request = self.authenticatedURLRequest(for: url,httpMethod: "GET")
URLSession.shared.dataTask(with: request) { data,response,error in
if let data = data {
let decoded = try! JSONDecoder.telemetryDecoder.decode(InsightDataTransferObject.self,from: data)
dispatchQueue.main.async { [weak self] in
guard let self = self else {
print("Self is gone")
return
}
var newInsightData = self.insightData
newInsightData[decoded.id] = decoded
self.insightData = newInsightData
}
}
}.resume()
}
}
// more retrieval functions for the other published properties
// ...
这是full file,但我很确定我在这个精简版中包含了所有相关部分
解决方法
首先,我假设内存会随着这条线的增长而增长:
self.insightData[decoded.id] = decoded
您为每次下载都存储了新值,并且除非重复 decoded.id
,否则似乎永远不会释放它们。这不是泄漏。那只是在内存中存储更多数据。
也就是说,如果您使用 while
循环对此进行测试,您应该预期内存会大幅增长,因为您永远不会耗尽自动释放池。当前运行循环周期完成时,自动释放池会被排空,但此 while
循环永远不会结束。所以你想创建一个新池:
while true {
@autoreleasepool {
api.getInsightData(for: insight,in: insightGroup,in: app)
sleep(1)
}
}