我早些时候发现我的游戏内存使用量只是在移动磁贴时才增加,但再也没有回落。由此,我可以判断出内存泄漏。
然后我开始使用Xcode Instruments,这是我的新手。因此,我遵循了this article中的许多内容,尤其是Recording Options
,然后设置了显示Call Tree
的模式。
我有两个功能,它们仅沿该行/列移动所有图块,然后在末尾克隆图块(使用node.copy()
),以便所有内容都可以“循环播放”,因此也就是项目名称。
我感觉瓦片克隆可能会导致某个保留周期,但是它存储在函数范围内的变量中。在克隆上运行SKAction
后,我使用copiedNode.removeFromParent()
从场景中删除了图块。
那么什么原因可能导致此内存泄漏?我可以找错地方了吗?
我已将这段代码简化为我认为必要的代码。
类顶部的声明:
/// Delegate to the game scene to reference properties.
weak var delegate: GameScene!
/// All the cloned tiles currently on the board.
private var cloneTiles = [SKSpriteNode]()
在移动磁贴中克隆磁贴功能:
/// A duplicate of the current tile.
let copiedNode = currentTile.node.copy() as! SKSpriteNode // Create copy
cloneTiles.append(copiedNode) // Add as a clone
delegate.addChild(copiedNode) // Add to the scene
let copiedNodeAction = SKAction.moveBy(x: movementDifference, y: 0, duration: animationDuration) // Create the movement action
// Run the action, and then remove itself
copiedNode.run(copiedNodeAction) {
self.cloneTiles.remove(at: self.cloneTiles.firstIndex(of: copiedNode)!)
copiedNode.removeFromParent()
}
立即移动图块的功能:
/// Move all tiles to the correct location immediately.
private func moveTilesToLocationImmediately() {
// Remove all clone tiles
cloneTiles.forEach { $0.removeFromParent() }
cloneTiles.removeAll()
/* Moves tiles here */
}
是否需要将某些内容声明为weak var
或其他内容?我知道保留周期是如何发生的,但是由于我从cloneTiles
数组中删除了克隆的tile引用,所以不知道为什么它存在于代码中。
Mark Szymczyk
帮助)这是我在调用堆栈中双击“移动磁贴”功能后发生的情况(请参见下面的答案):
这确认内存泄漏是由节点克隆以某种方式引起的,但是我仍然不知道为什么从cloneTiles
数组和场景中删除该节点后仍保留该节点。节点是否可能由于某种原因而无法从场景中移除?
请留下任何提示或问题,以便解决此问题!
我现在一直在尝试掌握Xcode Instruments,但是我仍然很努力地寻找这种内存泄漏。这是泄漏面板,可能会有所帮助:
即使尝试了[weak self]
,我仍然没有运气:
即使封包内的[weak self]
泄漏历史看起来还是一样。
当前,@matt
正在帮助我解决此问题。通过添加类似[unowned self]
的内容,我更改了几行代码:
// Determine if the tile will roll over
if direction == .up && movementDifference < 0 || direction == .down && movementDifference > 0 {
// Calculate where the clone tile should move to
movementDifference -= rollOverDistance
/// A duplicate of the current tile.
let copiedNode = currentTile.node.copy() as! SKSpriteNode // Create copy
cloneTiles.append(copiedNode) // Add as a clone
delegate.addChild(copiedNode) // Add to the scene
let copiedNodeAction = SKAction.moveBy(x: 0, y: movementDifference, duration: animationDuration) // Create the movement action
// Run the action, and then remove itself
copiedNode.run(copiedNodeAction) { [unowned self, copiedNode] in
self.cloneTiles.remove(at: self.cloneTiles.firstIndex(of: copiedNode)!).removeFromParent()
}
// Move the original roll over tile back to the other side of the screen
currentTile.node.position.y += rollOverDistance
}
/// The normal action to perform, moving the tile by a distance.
let normalNodeAction = SKAction.moveBy(x: 0, y: movementDifference, duration: animationDuration) // Create the action
currentTile.node.run(normalNodeAction) { [unowned self] in // Apply the action
if forRow == 1 { self.animationsCount -= 1 } // Lower animation count for completion
}
不幸的是,我无法将copiedNode
设为weak
属性,因为它总是立即nil
,并且unowned
导致在释放引用后读取引用时发生崩溃。如果有帮助,这也是Cycles & Roots
图:
谢谢您的帮助!
答案 0 :(得分:1)
我可以在乐器方面提供一些帮助。如果您在仪器调用树中双击{{1}}条目,仪器将向您显示分配泄漏内存的代码行。窗口底部是“呼叫树”按钮。如果单击该按钮,则可以反转调用树并隐藏系统库。这样做将使在调用树中查找代码更加容易。
您可以在以下文章中了解有关乐器的更多信息:
答案 1 :(得分:1)
我对您管理复制节点的方式非常怀疑;您可能会过早释放它,并且只有保留周期才阻止您发现此错误。但是,让我们集中精力打破保留周期。
您要做的是将所有内容都放入动作方法weak
中,以使动作方法不会引起任何强烈的捕获。然后,在操作方法中,您要立即保留那些弱引用,以免它们从您的下方消失。这就是所谓的“弱劲舞”。像这样:
copiedNode.run(copiedNodeAction) { [weak self, weak copiedNode] in
if let `self` = self, let copiedNode = copiedNode {
// do stuff here
// be sure to log so you know we arrived here at all, as we might not
}
}