ios - 无法使用 Xcode Instruments 定位内存泄漏
问题描述
问题
我之前发现,我在游戏中的内存使用只有在移动瓷砖时才会上升,但它再也没有下降过。由此,我可以看出有内存泄漏。
然后我开始使用我非常陌生的 Xcode Instruments。所以我从这篇文章中遵循了很多东西,尤其是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 */
}
有什么我需要声明为 aweak 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
图表:
感谢您的任何帮助!
解决方案
我可以在仪器方面提供一些帮助。如果您双击moveHorizontally
Instruments 调用树中的条目,Instruments 将向您显示分配泄漏内存的代码行。窗口底部是一个调用树按钮。如果单击它,您可以反转调用树并隐藏系统库。这样做将使您更容易在调用树中找到您的代码。
您可以在以下文章中了解有关 Instruments 的更多信息:
推荐阅读
- excel - 替代WEBSERVICE公式(VBA函数)
- java - 如何打开电池使用活动?
- django - django admin queryset 在字段中显示相关对象
- java - 我需要传递一个条件作为参数
- java - 如何在两个表中进行记录
- python - 为什么无论阈值水平如何,小波去噪都会产生相同的结果?
- php - 当定义条件时,用tableB的idcolumn更新tableA的列
- python - RuntimeError: 给定组=1,大小为 [64, 3, 3, 3] 的权重,预期输入 [4, 5000, 5000, 3] 有 3 个通道,但有 5000 个通道
- r - 如何在 r 编程中从 50+ 级的巨大因子变量中创建因子?
- python - 如何根据变量在列表中创建 X 元素?