【发布时间】:2019-06-20 21:07:37
【问题描述】:
问题
我之前发现我在游戏中的内存使用量仅在移动瓷砖时才会增加,但它再也没有下降。由此,我可以看出内存泄漏。
然后我开始使用 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 数组中删除了克隆的磁贴引用。
泄漏发生的大致位置(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 图表:
感谢您的帮助!
【问题讨论】:
-
看看这个:stackoverflow.com/questions/27007350/… 您需要在运行 SKAction 中引用的所有外部事物都是弱的。所以不仅仅是自我,也可能是复制的节点。
-
整个问题似乎是这种奇怪的方式,您坚持对复制的节点的额外引用并且还让节点自行删除。我觉得这非常令人困惑和奇怪。我很难对此提出积极的建议,但我建议在 run 动作中使用弱强舞蹈,以便 if 值到达动作处理程序,它们被保留为 local 强变量。
-
@matt 是否有另一种方法可以推荐,将克隆节点存储在
cloneTiles数组中? -
@matt 我也不确定你所说的“弱强舞”是什么意思。你的意思是我从动作中删除
[unowned self, copiedNode],然后在里面写let copiedNodeRef = copiedNode? -
@matt 上面的注释做了可选绑定,这是否意味着我需要将
copiedNode声明为weak var?非常感谢您的帮助,这意义重大!
标签: ios xcode memory-management memory-leaks xcode-instruments