我创建了一个 SCNNode
的子类。它由几个子节点组成。
我已经宣布了一个方法,即。 soundCasual()
将 SCNAudioPlayer
添加到此类的实例中。当调用此方法时,一切都按预期工作并且正在播放音频。只要该节点被点击(手势),就会在该节点上调用此方法。
代码:
class MyNode: SCNNode {
let wrapperNode = SCNNode()
let audioSource5 = SCNAudioSource(fileNamed: "audiofile.mp3")
override init() {
super.init()
if let virtualScene = SCNScene(named: "MyNode.scn", inDirectory: "Assets.scnassets/Shapes/MyNode") {
for child in virtualScene.rootNode.childNodes {
wrapperNode.addChildNode(child)
}
}
}
func soundCasual() {
DispatchQueue.global(qos: .userInteractive).async { [weak self] in
if let audioSource = self?.audioSource5 {
let audioPlayer = SCNAudioPlayer(source: audioSource)
self?.wrapperNode.removeAllAudioPlayers()
self?.wrapperNode.addAudioPlayer(audioPlayer)
}
}
}
}
工具(分配)中的问题
当我分析我的整个代码库(这是其他几件事)时,我发现每当我点击该节点时,SCNAudioPlayer 的分配计数都会增加 1,同时在 Instruments 中进行分析。但所有的增加都是持久的。根据 SCNAudioPlayer
的定义,我假设这个播放器在播放后被删除,这就是增量应该在 transient 分配中的原因,但它不是这样工作的。这就是为什么我在将 SCNAudioPlayer 添加到节点之前尝试使用 removeAllAudioPlayers()
,如您在 soundCasual()
的代码中所见。但问题依然存在。
在拍摄此快照之前,我已经点击了该节点大约 17 次,它还显示 17 次针对 SCNAudioPlayer 的持久分配。
注意:SCNAudioSource
应该是 10,因为我在应用程序中使用了 10 个音频源
我的应用程序中的所有其他 SCNNode 都会发生这种情况,而且不会失败。 请帮忙,因为我无法理解我到底错过了什么。
编辑
按照建议,我将 init()
更改为
let path = Bundle.main.path(forResource: "Keemo", ofType: "scn", inDirectory: "Assets.scnassets/Shapes/Keemo")
if let path = path , let keemo = SCNReferenceNode(url: URL(fileURLWithPath: path)) {
keemo.load()
}
func soundPlay() {
DispatchQueue.global(qos: .userInteractive).async { [weak self] in
if let audioSource = self?.audioSourcePlay {
audioSource.volume = 0.1
let audioPlayer = SCNAudioPlayer(source: audioSource)
self?.removeAllAudioPlayers()
self?.addAudioPlayer(audioPlayer)
}
}
}
尽管如此,Instruments 中的分配显示 audioPlayers 是持久的。虽然在检查 node.audioPlayers
时它显示在某一时刻,只有一个 audioPlayer 节点附加。
编辑
当我使用 XCode 默认创建的 Scenekit 应用程序中附加的样板代码库时,即使在一个简单的情况下也会出现此问题。因此,此问题已作为错误提交给 Apple。 https://bugreport.apple.com/web/?problemID=43482539
解决方法
我用的是AVAudioPlayer而不是SCNAudioPlayer,不是完全一样的东西,但至少这样内存不会导致崩溃。
最佳答案
我不熟悉 SceneKit,但根据我使用 UIKit 和 SpriteKit 的经验,我怀疑您对 wrapperNode
和 virtualScene
的使用会干扰垃圾收集器。
我会尝试删除 wrapperNode
并将所有内容添加到 self
(因为 self
是一个 SCNNode
)。
您的场景中使用了哪个节点? self
还是 wrapperNode
?并且您的示例代码 wrapperNode
未添加到 self
因此它实际上可能是也可能不是场景的一部分。
此外,您可能应该使用 SCNReferenceNode
而不是您正在使用的虚拟场景。
!!!此代码尚未经过测试!!!
class MyNode: SCNReferenceNode {
let audioSource5 = SCNAudioSource(fileNamed: "audiofile.mp3")
func soundCasual() {
DispatchQueue.global(qos: .userInteractive).async { [weak self] in
if let audioSource = self?.audioSource5 {
let audioPlayer = SCNAudioPlayer(source: audioSource)
self?.removeAllAudioPlayers()
self?.addAudioPlayer(audioPlayer)
}
}
}
}
// If you programmatically create this node, you'll have to call .load() on it
let referenceNode = SCNReferenceNode(URL: referenceURL)
referenceNode.load()
嗯!
关于ios - Xcode Instrument 中 SCNNodes 上 SCNAudioPlayer 的持久化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51431512/