【问题标题】:Identifying memory leak in Swift GCD.async call识别 Swift GCD.async 调用中的内存泄漏
【发布时间】:2019-08-19 21:47:01
【问题描述】:

关于我的应用的一些背景知识:我正在绘制地图。当用户移动地图时,我执行数据库查询。我首先进行 rTree 查询以查找将在当前视口中绘制的特征。获得这些 ID 后,我将执行第二次数据库查询以从数据库中提取特征 (geojson)。我快速检查该项目是否已被绘制,如果没有,我会执行addChild 以在地图上呈现该特征。我想通过 GCD 在后台查找这些数据库,以便用户可以顺利移动地图。我已经实现了这一点,但内存使用量迅速增长到 1gb,而如果我在主线程中完成所有工作,它使用大约 250mb(我可以接受)。我假设由于关闭的使用而没有清理某些东西。感谢您对内存泄漏原因的任何见解。

public func drawItemsInBox(boundingBox: [Double]) {
        DispatchQueue.global(qos: .background).async { [weak self] in
            guard let self = self else {
                return
            }

            var drawItems: [Int64] = []
            let table = Table("LNDARE_XS")
            let tableRTree = Table("LNDARE_XS_virtual")
            let coords = Expression<String?>("coords")
            let foid = Expression<String>("foid")
            let rTree = Expression<Int64>("rTree")
            let minX = Expression<Double>("minX")
            let maxX = Expression<Double>("maxX")
            let minY = Expression<Double>("minY")
            let maxY = Expression<Double>("maxY")
            let id = Expression<Int64>("id")

            // find all the features to draw via an rTree query
            for row in try! self.db.prepare(tableRTree.filter(maxX >= boundingBox[0] && minX <= boundingBox[1] && maxY >= boundingBox[2] && minY <= boundingBox[3])) {
                drawItems.append(row[id])
            }

            do {
                // get all the features geojson data
                let query = table.filter(drawItems.contains(rTree))
                for row in try self.db.prepare(query) {

                    // skip drawing if the feature already exists on the map
                    if self.featureTracking["LNDARE_XS"]?[Int64(row[foid])!] == nil {

                        // convert the database string to an array of coords
                        var toBeRendered:[CGPoint] = []
                        let coordsArray = row[coords]!.components(separatedBy: ",")

                        for i in 0...(coordsArray.count / 2) - 1 {
                            toBeRendered.append(CGPoint(x: (Double(coordsArray[i*2])!), y: (Double(coordsArray[(i*2)+1])!)))
                        }

                        let linearShapeNode = SKShapeNode(points: &toBeRendered, count: toBeRendered.count)
                        linearShapeNode.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
                        linearShapeNode.lineWidth = 0
                        linearShapeNode.fillColor = NSColor.black

                        // append the featureId for tracking and call addChild to draw
                        self.scaleLayer.addChild(linearShapeNode)
                        self.featureTracking["LNDARE_XS"]?[Int64(row[foid])!] = linearShapeNode
                    }
                }
            } catch {
                // catch
            }
        }
    }

【问题讨论】:

  • 尝试使用自动释放池

标签: swift memory-leaks sprite-kit grand-central-dispatch sqlite.swift


【解决方案1】:

也许尝试使用自动释放池,因为您不在主线程上

public func drawItemsInBox(boundingBox: [Double]) {
    DispatchQueue.global(qos: .background).async { [weak self] in
        guard let self = self else {
            return
        }

        var drawItems: [Int64] = []
        let table = Table("LNDARE_XS")
        let tableRTree = Table("LNDARE_XS_virtual")
        let coords = Expression<String?>("coords")
        let foid = Expression<String>("foid")
        let rTree = Expression<Int64>("rTree")
        let minX = Expression<Double>("minX")
        let maxX = Expression<Double>("maxX")
        let minY = Expression<Double>("minY")
        let maxY = Expression<Double>("maxY")
        let id = Expression<Int64>("id")

        // find all the features to draw via an rTree query
        for row in try! self.db.prepare(tableRTree.filter(maxX >= boundingBox[0] && minX <= boundingBox[1] && maxY >= boundingBox[2] && minY <= boundingBox[3])) {
            drawItems.append(row[id])
        }

        do {
            // get all the features geojson data
            let query = table.filter(drawItems.contains(rTree))
            for row in try self.db.prepare(query) {
                autoreleasepool{
                    // skip drawing if the feature already exists on the map
                    if self.featureTracking["LNDARE_XS"]?[Int64(row[foid])!] == nil {

                        // convert the database string to an array of coords
                        var toBeRendered:[CGPoint] = []
                        let coordsArray = row[coords]!.components(separatedBy: ",")

                        for i in 0...(coordsArray.count / 2) - 1 {
                        toBeRendered.append(CGPoint(x: (Double(coordsArray[i*2])!), y: (Double(coordsArray[(i*2)+1])!)))
                        }

                        let linearShapeNode = SKShapeNode(points: &toBeRendered, count: toBeRendered.count)
                        linearShapeNode.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
                        linearShapeNode.lineWidth = 0
                        linearShapeNode.fillColor = NSColor.black

                        // append the featureId for tracking and call addChild to draw
                        self.scaleLayer.addChild(linearShapeNode)
                        self.featureTracking["LNDARE_XS"]?[Int64(row[foid])!] = linearShapeNode
                    }
                }
            }
        } catch {
            // catch
        }
    }
}

【讨论】:

  • 很遗憾没有影响。感谢您的建议。
【解决方案2】:

也许改toBeRendered可以省一些:

  var toBeRendered:[CGPoint] = []
            for row in try self.db.prepare(query) {

                // skip drawing if the feature already exists on the map
                if self.featureTracking["LNDARE_XS"]?[Int64(row[foid])!] == nil {

                    // convert the database string to an array of coords
                    toBeRendered.removeAll()
                    let coordsArray = row[coords]!.components(separatedBy: ",")

                    for i in 0...(coordsArray.count / 2) - 1 {
                        toBeRendered.append(CGPoint(x: (Double(coordsArray[i*2])!), y: (Double(coordsArray[(i*2)+1])!)))
                    }

【讨论】:

  • 您能解释一下将变量移动到不同范围的目的吗?您是说它创建了一个导致内存泄漏的强引用还是一般的性能增强?
  • Toberendered.append() 是一个耗时的操作。因此,您可以通过 init 一次优化此数组并重用它,这样您就无需等待稍后可能会出现的自动释放。你也可以在 init 之后添加一个 reservecapcity 命令,它可以为你的变量分配一个块区域,可以加快 30% 的速度。
猜你喜欢
  • 2013-03-19
  • 2011-03-21
  • 2011-03-28
  • 1970-01-01
  • 2015-02-11
  • 2020-03-19
  • 2016-11-23
  • 1970-01-01
相关资源
最近更新 更多