Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SceneKit leaking when cloning a node

I have an asset loading and caching singleton defined as such:

class AssetLoader {
    fileprivate var rootNodes = Dictionary<String, SCNNode>()

    static let sharedInstance = AssetLoader()

    fileprivate init() {
    }

    func rootNode(_ named: String) -> SCNNode {
        if self.rootNodes[named] != nil {
            return self.rootNodes[named]!.clone()
        } else {
            let scene = SCNScene(named: "art.scnassets/\(named).scn")
            self.rootNodes[named] = scene!.rootNode
            return self.rootNodes[named]!.clone()
        }
    }
}

I am using it to make my scene building faster. I'm creating assets from extensions as such:

extension CAAnimation {
    class func animationWithScene(named: String) -> CAAnimation? {
        unowned let rootNode = AssetLoader.sharedInstance.rootNode(named)
        var animation: CAAnimation?

        rootNode.enumerateChildNodes({ (child, stop) in
            if child.animationKeys.count > 0 {
                animation = child.animation(forKey: child.animationKeys.first!)
                stop.initialize(to: true)
            }
        })
        return animation
    }
}

extension SCNNode {
    class func nodeWithScene(named: String) -> SCNNode? {
        unowned let rootNode = AssetLoader.sharedInstance.rootNode(named)
        let node = SCNNode()

        for child in rootNode.childNodes {
            node.addChildNode(child)
        }

        node.eulerAngles = SCNVector3(x: Float(-M_PI_2), y: 0, z: 0)
        node.scale = SCNVector3Make(kMeshScale, kMeshScale, kMeshScale)

        return node
    }
}

Instruments is saying I'm leaking memory like crazy on each calls to clone(). I tried using weak and unowned wherever I could without causing crashes and it doesn't change anything. Anyone has a clue? Is that a bug in SceneKit?

Thanks

like image 730
Ghislain Leblanc Avatar asked Oct 29 '22 11:10

Ghislain Leblanc


1 Answers

If I understand correctly you keep your original nodes in the rootNodes Dictionary of your AssetLoader and return a clone of those in the rootNode func.

My architecture is similar and my issue was the following : when I would remove the cloned node from the scene tree the memory wouldn't get released. Is that your problem?

I fixed the issue by adding an "unload" func in my singleton to nullify the original nodes when removing the cloned nodes from the scene tree. That fixed my memory issues.

With your code that would look something like :

func unloadRootNode(_ named: String) {
    rootNodes.removeValue(forKey: named)
}
like image 177
Florent Alexandre Avatar answered Nov 15 '22 06:11

Florent Alexandre