swift - SpriteKit 场景切换时的内存问题

标签 swift memory sprite-kit

好吧,回过头来测试时,我遇到了一次没有错误的随机崩溃,我不知道为什么。所以我开始分析事物并得出以下数据。 Data

我的内存使用率似乎越来越高,直到达到稳定水平。请注意,开始时总曲率的斜率如何比后来更大。 (您可能会注意到,这是我第一次进入并分析这类事情)。

现在游戏中发生的事情基本上是两个屏幕。 1.菜单:这个屏幕有很多纹理,但除了有按钮玩游戏外什么都不做 2. 游戏:这有很多纹理并且占用了大量的 CPU,因为它是真正的游戏。 3.死亡:这个画面有一个 Assets ,它是一个让你重玩游戏的按钮。这不应该使用太多内存或 cpu。但是它仍然有内存。对我来说,无论发生什么“内存泄漏”,这都是尖叫声。

如果你看一下图表,基本上游戏中发生的事情是菜单启动,第一个峰值是加载实际游戏,然后我死了。从那时起,我就在游戏和死亡屏幕之间切换,每个尖峰信号都表示游戏场景正在加载。

如果这个数据是我预测的那样,你会发现死亡屏幕的内存使用量非常小,然后又回到游戏内存使用量之间。

这个故事的寓意是,我很确定在切换场景后 sprite kit 没有正确清理,如果可能的话我需要知道原因。

顺便说一句,为了切换场景,我使用的是maxkargin详细介绍的方法 here

顺便说一句,我正在快速使用 sprite kit 和 SKScenes 以及 SKSpriteNodes

非常感谢!

最佳答案

这有几个原因,我的游戏也遇到过类似的问题。如果操作正确,则无需删除纹理等内容。在每次场景更改时删除纹理也不理想,您希望将它们保留在内存中以提高性能,这样它们就不必每次都重新加载。

这是一个基本 list ,您可以使用它来查看是否造成了内存泄漏。

1) 为每个场景/类添加带有打印语句的 deinit 方法。如果 deinit 被调用,您的场景就会正确释放。

 deinit {
    print("Deinit GameScene")
  }

2) 您是否通过在 2 个类之间创建引用来在某处创建强引用循环?

强引用循环的经典 Apple 示例

 class Person {
    var dog: Dog?
  }

 class Dog {
   var person: Person?
 }

要修复它,您必须使这 2 个属性中的 1 个变弱

  class Person {
     var dog: Dog?
  }

  class Dog {
     weak var person: Person?
  }

另外一个好的做法是在不再需要时将它们设置为 nil。

 person = nil

也许可以查看 google 和 Apple Swift 文档,了解如何处理这个问题。前段时间我也问过类似的问题

Swift SpriteKit ARC for dummies

3) 你在使用闭包吗?它们会导致内存泄漏。

SpriteKit 中更常见的情况是这 2 个示例,它们可能/将导致内存泄漏并使您的场景无法解除分配。 (action 2 是一个捕获 self 的闭包)

 // Example 1
 let action1 = SKAction.wait(forDuration: 1)
 let action2 = SKAction.run(someMethod)
 let sequence = SKAction.sequence([action1, action2])
 run(SKAction.repeatForever(sequence))

 // Example 2
 let action1 = SKAction.wait(forDuration: 1)
 let action2 = SKAction.run {
      self.someMethod()
 }
 let sequence = SKAction.sequence([action1, action2])
 run(SKAction.repeatForever(sequence))

一个好的经验法则是,当编译器强制您使用 self 时,您很可能会在不使用 weak/unowned 的情况下造成内存泄漏。

因此,要修复上述 2 个 SKAction 示例,您可以确保在更改场景时始终删除所有操作,或者 IMO 更好的做法是将代码更改为此,以避免首先造成内存泄漏。

 let action1 = SKAction.wait(forDuration: 1)
 let action2 = SKAction.run { [weak self] in
      self?.someClassMethod()
  }
 let sequence = SKAction.sequence([action1, action2])
 run(SKAction.repeatForever(sequence))

请注意,在上面的所有示例中,您也可以这样写

 .... { [unowned self] in

而且你不需要使用 ?在闭包中

 self.someMethod()

当你使用 unowned 时,你基本上会说 self 永远不会为 nil,如果在调用闭包时它实际上为 nil,则可能会导致崩溃。如果你使用 weak self 你告诉编译器 self 可能在调用闭包之前变为 nil 因此 self 是一个可选的以避免崩溃。

我认为几乎总是使用“weak”而不是“unowned”来避免这种情况更好。在我的一个游戏中,我在从 iTunes 获取 StoreKit 产品时调用的闭包中使用了 unowned self。这导致了我微妙的崩溃,因为我可以在调用闭包之前退出 SKScene。如果你使用 weak self 你不会崩溃,因为你使用了可选值。

希望对您有所帮助

关于swift - SpriteKit 场景切换时的内存问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32164412/

相关文章:

swift - 如何将在 assets.xassets 中创建的 Sprite Atlas 的纹理格式更改为 RGBA4444

ios - textViewDidChange 仅在键入完整字符串时发生变化。我想让它检查每个字符

swift - 为 AVPlayer 视频添加字幕

java - 在java中,如果我扩展一个类并且在重新定义每个函数时不使用 super 字段,该字段是否仍然使用内存?

python - 短标量中的 Numpy 溢出

animation - iOS7 SpriteKit 如何对两个或多个 Sprite 节点 child 的动画进行分组?

swift - 使用 NSFetchedResultsController 从 UITableView 中删除行

ios - 将 4 个不同数组的内容按相同顺序排序

c# - c# 服务中的巨大内存爆裂,可能是什么原因?

ios - SpriteKit 粒子发射器改变颜色