Swift 如何解决内存问题 "Leak"

Swift How to solve a Memory "Leak"

我遇到了我不明白的内存“泄漏”。我有一个缓存 class,其目的是限制大量数据项的实例数。即使它存储了这些项目的 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) }
    }
}

您有正在创建的自动释放对象。插入一个 autoreleasepool 以定期排空池,例如:

func performOnSelection(perform action: @escaping (Item) -> Void) {
    ...

    autoreleasepool {
        perform(item)
    }

    performOnSelection() { item in
        autoreleasepool {
            let _ = Item(item)
        }
    }

    ...
}

func duplicate(times: Int) {
    for index in 0..<times {
        print(index)
        autoreleasepool { [self] in
            Item.selection.append(Item(self))
        }
    }
}

例如没有 autoreleasepool:

还有:

当它不处理所有那些挥之不去的自动释放对象时,它会变得更快。峰值内存使用量从 3.4gb 变为 47mb。