SceneKit:过多的内存持续存在 [英] SceneKit: too much memory persisting

查看:155
本文介绍了SceneKit:过多的内存持续存在的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在这里没有主意,SceneKit正在堆积在内存上,我才刚刚开始.我正在显示SNCNodes,它们存储在数组中,所以我可以分离分子的各个组成部分以进行动画处理.这些树模拟的分子最终可能要显示50个,每个章节"说一个.问题是,当我转到另一章时,前几章中的分子仍保留在内存中.

I’m out of ideas here, SceneKit is piling on the memory and I’m only getting started. I’m displaying SNCNodes which are stored in arrays so I can separate components of the molecule for animation. These trees model molecules of which I will ultimately have maybe 50 to display, say one per "chapter". The issue is when I move to another chapter the molecules from previous chapters persist in memory.

分子节点是子节点的树.出于定向目的,大约一半的节点是空容器.否则,几何形状为SCNPrimitives(球体,胶囊和圆柱体).每个几何都有一个镜面反射和一个由UIColor组成的漫射material,不使用任何纹理.

The molecule nodes are trees of child nodes. About half of the nodes are empty containers for orientation purposes. Otherwise, the geometries are SCNPrimitives (spheres, capsules, and cylinders). Each geometry has a specular and a diffuse material consisting of a UIColor, no textures are used.

在应用程序首次启动时,这些分子是由代码构建的,并存档到字典中.然后,在随后的启动中,将已存档的字典读入本地字典以供VC使用. (为简便起见,我在这篇文章中删除了安全功能.)

When the app first boots, these molecules are constructed from code and archived into a dictionary. Then, and on subsequent boots, the archived dictionary is read into a local dictionary for use by the VC. (I’m removing safety features in this post for brevity.)

moleculeDictionary = Molecules.readFile() as! [String: [SCNNode]]

当一章想要显示一个分子时,它会调用一个特定的函数,该函数将从本地字典中将给定分子所需的组件加载到本地的SCNNode属性中.

When a chapter wants to display a molecule it calls a particular function that loads the needed components for a given molecule from the local dictionary into local SCNNode properties.

// node stores (reuseable)
var atomsNode_1 = SCNNode() 
var atomsNode_2 = SCNNode()
        . . .

func lysozyme() {   // called by a chapter to display this molecule 
        . . .
    components = moleculeDictionary["lysozyme"]

    atomsNode_1 = components[0]         // protein w/CPK color
    baseNode.addChildNode(atomsNode_1)
    atomsNode_2 = components[2]         // NAG
    baseNode.addChildNode(atomsNode_2)
        . . .
 }

在显示下一个分子之前,我调用清理"功能:

Before the next molecule is to be displayed, I call a "clean up" function:

atomsNode_1.removeFromParentNode()
atomsNode_2.removeFromParentNode()
        . . .

当我研究仪器时,大部分,肿的内存是C3DMeshCreateFromProfile调用的32 kB块和C3DMeshCreateCopyWithInterleavedSources的80 kB块.

When I investigate in instruments, most of the bloated memory is 32 kB chunks called by C3DMeshCreateFromProfile and 80 kB chunks of C3DMeshCreateCopyWithInterleavedSources.

我也需要跟踪泄漏,这些泄漏可以追溯到存档的NSKeyedUnarchiver解码.因此,我也需要处理这些问题,但是它们只是累积每个分子调用的内存使用的一小部分.

I also have leaks I need to trace which are traceable to the NSKeyedUnarchiver decoding of the archive. So I need to deal with these as well but they are a fraction of the memory use that’s accumulating each molecule call.

如果我返回以前查看过的分子,则内存使用量不会进一步增加,它会累积并持续存在.

If I return to a previously viewed molecule, there is no further increase in memory usage, it all accumulates and persists.

我尝试将atomsNode_1及其亲属声明为可选,然后在清理时将其设置为nil.没有帮助我已经尝试过在清理功能中

I’ve tried declaring atomsNode_1 and its kin as optionals then setting them to nil at clean up time. No help. I’ve tried, in the clean up function,

atomsNode_1.enumerateChildNodesUsingBlock({
    node, stop in
    node.removeFromParentNode()
})

好吧,内存回落了,但是现在看来节点已从加载的字典中永久删除了.该死的参考类型!

Well, the memory goes back down but the nodes seem to now be permanently gone from the loaded dictionary. Damn reference types!

因此,也许我需要一种方法来存档[SCNNode]数组,以便取消存档并分别检索它们.在这种情况下,完成后我将清除它们的内存,并在重新访问该分子时从存档中重新加载它们.但是我还不知道该怎么做.我会很乐意就此发表评论,然后再花更多的时间感到沮丧.

So maybe I need a way to archive the [SCNNode] arrays in such a way as to unarchive and retrieve them individually. In this scenario I would clear them out of memory when done and reload from the archive when revisiting that molecule. But I know not yet how to do either of these. I’d appreciate comments about this before investing more time to be frustrated.

推荐答案

球体,胶囊和圆柱体都具有相当密集的网格.您需要所有这些细节吗?尝试减少各种段计数属性(segmentCountradialSegmentCount等).作为快速测试,用SCNPyramid代替所有原始类型(即向量计数最低的原始类型).如果这是一个因素,您应该会看到内存使用量的显着减少(这看起来很丑陋,但是会立即向您提供有关是否在可用轨道上的反馈).您可以使用长的SCNBox代替圆柱体吗?

Spheres, capsules, and cylinders all have fairly dense meshes. Do you need all that detail? Try reducing the various segment count properties (segmentCount, radialSegmentCount, etc). As a quick test, substitute SCNPyramid for all of your primitive types (that's the primitive with the lowest vector count). You should see a dramatic reduction in memory use if this is a factor (it will look ugly, but will give you immediate feedback on whether you're on a usable track). Can you use a long SCNBox instead of a cylinder?

另一个优化步骤是使用SCNLevelOfDetail,以在对象距离较远时允许替代的低顶点数几何形状.这将比简单地统一减少段数更多,但是如果您有时需要更多的细节,将会有所收获.

Another optimization step would be to use SCNLevelOfDetail to allow substitute, low vertex count geometry when an object is far away. That would be more work than simply reducing the segment counts uniformly, but would pay off if you sometimes need greater detail.

使用节点层次结构来执行此操作,而不是自己管理数组中的组件.将每个分子或分子的可动画片段创建为SCNNodes的树.给它起个名字.制作flattenedClone.现在存档.需要时从存档中读取节点树;不用担心节点数组.

Instead of managing the components yourself in arrays, use the node hierarchy to do that. Create each molecule, or animatable piece of a molecule, as a tree of SCNNodes. Give it a name. Make a flattenedClone. Now archive that. Read the node tree from archive when you need it; don't worry about arrays of nodes.

考虑编写两个程序.一种是您的iOS程序,用于操纵/显示分子.另一个是Mac(或iOS?)程序,该程序生成分子节点树并将其存档.这样一来,您就可以一堆SCNNode树存档,您可以将它们作为资源嵌入到显示程序中,而无需即时生成.

Consider writing two programs. One is your iOS program that manipulates/displays the molecules. The other is a Mac (or iOS?) program that generates your molecule node trees and archives them. That will give you a bunch of SCNNode tree archives that you can embed, as resources, in your display program, with no on-the-fly generation.

使用swift进行场景工具包内存管理的答案注意需要消除纹理"(materialsfirstMaterial属性?)以释放节点.似乎值得一看,尽管既然您只是在使用UIColor,我怀疑这是一个因素.

An answer to scene kit memory management using swift notes the need to nil out "textures" (materials or firstMaterial properties?) to release the node. Seems worth a look, although since you're just using UIColor I doubt it's a factor.

这里是创建复合节点并将其归档的示例.在实际代码中,您已将归档与创建分开.另请注意,使用细长的盒子来模拟一条线.尝试将倒角半径设置为0!

Here's an example of creating a compound node and archiving it. In real code you'd separate the archiving from the creation. Note also the use of a long skinny box to simulate a line. Try a chamfer radius of 0!

extension SCNNode {

public class func gizmoNode(axisLength: CGFloat) -> SCNNode {
    let offset = CGFloat(axisLength/2.0)
    let axisSide = CGFloat(0.1)
    let chamferRadius = CGFloat(axisSide)

    let xBox = SCNBox(width: axisLength, height: axisSide, length: axisSide, chamferRadius: chamferRadius)
    xBox.firstMaterial?.diffuse.contents = NSColor.redColor()
    let yBox = SCNBox(width: axisSide, height: axisLength, length: axisSide, chamferRadius: chamferRadius)
    yBox.firstMaterial?.diffuse.contents = NSColor.greenColor()
    let zBox = SCNBox(width: axisSide, height: axisSide, length: axisLength, chamferRadius: chamferRadius)
    zBox.firstMaterial?.diffuse.contents = NSColor.blueColor()
    let xNode = SCNNode(geometry: xBox)
    xNode.name = "X axis"
    let yNode = SCNNode(geometry: yBox)
    yNode.name = "Y axis"
    let zNode = SCNNode(geometry: zBox)
    zNode.name = "Z axis"

    let result = SCNNode()
    result.name = "Gizmo"
    result.addChildNode(xNode)
    result.addChildNode(yNode)
    result.addChildNode(zNode)
    xNode.position.x = offset
    yNode.position.y = offset
    zNode.position.z = offset

    let data = NSKeyedArchiver.archivedDataWithRootObject(result)
    let filename = "gizmo"

    // Save data to file
    let DocumentDirURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)

    // made the extension "plist" so you can easily inspect it by opening in Finder. Could just as well be "scn" or "node"
    // ".scn" can be opened in the Xcode Scene Editor
    let fileURL = DocumentDirURL.URLByAppendingPathComponent(filename).URLByAppendingPathExtension("plist")
    print("FilePath:", fileURL.path)

    if (!data.writeToURL(fileURL, atomically: true)) {
        print("oops")
    }
    return result
}
}   

这篇关于SceneKit:过多的内存持续存在的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆