Ghislain Leblanc
Ghislain Leblanc

Reputation: 194

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

Upvotes: 5

Views: 451

Answers (1)

Florent Alexandre
Florent Alexandre

Reputation: 136

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)
}

Upvotes: 2

Related Questions