Swift SpriteKit ARC 傻瓜版

标签 swift reference sprite-kit instruments cycle

我一直在努力理解强引用循环,但我很挣扎。我一直在阅读苹果和某些网站上的文档,我觉得它们并没有真正解决我的问题。

我知道你必须使用weak和unowned,具体取决于对象是否可以为零。所以说你必须有 2 个这样的类(class)

class Person {
   var dog: Dog?
  ....
}

class Dog {
  weak var person: Person?
}

相互引用我知道其中一个必须使用weak/unowned。这是大多数教程中看到的经典示例。

我也见过这样的例子,它们对我来说也很有意义

class Person {

 unowned let gameScene: GameScene

 init(scene: GameScene) {
   self.gameScene = scene 
  ....
 }

我还了解到,如果不使其无效,NSTimers 可能会导致强引用循环。我没有使用 NSTimers,所以这应该不是问题。

我还了解到协议(protocol)也会导致内存泄漏,因此处理它们的方法是将其设为类协议(protocol)

protocol TestDelegate: class { }

我试图引用协议(protocol)的地方使其成为一个弱属性

 class: SomeClass {

  weak var myDelegate: TestDelegate?

 }

最后我知道了闭包,我可以像这样捕获 self

SKAction.runBlock { [unowned self] in
   self.player.runAction....
}

但是,当谈到我的实际 spritekit 游戏时,我似乎到处都有引用循环,即使对于简单的类,我也确信不会引用另一个类,而且我不明白为什么。

所以我的主要问题是

1) 是所有属性都会创建强引用循环还是仅在类初始化之前创建全局属性?

2)还有哪些其他事情可能会在我的简单类中创建强引用循环?

例如,我正在使用一个创建平台的类

class Platform: SKSpriteNode {

 /// basic code to create platforms 
 /// some simple methods to rotate platforms, move them left or right with SKActions.

现在我为陷阱、敌人等提供了类似的类。同样,它们通常只是设置 Sprite 属性(物理主体等),并有一些方法来为它们设置动画或旋转它们。它们没有什么特别的,特别是在引用其他类或场景方面。

在我的游戏场景中,我有一种创建平台的方法(对于敌人、陷阱等来说是相同的)

func createPlatform() {

 let platform1 = Platform(.....
 platformNode.addChild(platform1)

 let platform2 = Platform(....
 platformNode.addChild(platform2)

 let platform3 = Platform(...
 platformNode.addChild(platform3)

 // platform node is just a SKNode in the gameScene to help maintain the difference zPositions of my objects.

}

当我运行分配时,我可以看到 3 个平台中只有 1 个进入 transient ,当我更改为 menuScene 时,2 个保持持久状态。奇怪的是,总是只有 1 个被删除,无论我更改顺序或创建/删除某些平台都无关紧要。所以看起来他们正在创建强引用,除了 1。所以如果我重玩我的关卡几次,我可以很快在内存中拥有 50-100 个持久平台。因此,我的场景也没有被 deinit,这会浪费更多的内存。

我的另一个例子是

class Flag {
  let post: SKSpriteNode
  let flag: SKSpriteNode

  init(postImage: String, flagImage: String) {

       post = SKSpriteNode(imageNamed: postImage)
       ...


       flag = SKSpriteNode(imageNamed: flagImage)
       ...
       post.addChild(flag)
  }
}

和同样的问题。我在场景中创建了一些标志,有时标志不会被删除,有时会被删除。同样,此类不引用任何场景或自定义类,它只是创建一个 Sprite 并为其设置动画。

在我的场景中,我有一个 flag 的全局属性

 class GameScene: SKScene {

   var flag: Flag!

  func didMoveToView... 

 }

如果标志本身不引用 GameScene,为什么会创建强引用?我也无法使用

weak var flag: Flag!

因为一旦标志初始化,我就会崩溃。

这样做时我是否遗漏了一些明显的东西? 有没有一个好的技巧可以在仪器中找到它们,因为这对我来说似乎很疯狂。 这让我感到困惑,主要是因为我的类非常简单,并且不引用其他自定义类、场景、viewController 等。

最佳答案

感谢您的所有回答,特别是 appzYourLift 和您的详细回复。

我一整天都在深入研究我的 spriteKit 项目,也在 Playground 上进行了很多实验,我的游戏现在完全没有泄漏,没有找到强大的引用循环。

实际上,我在仪器中看到的许多泄漏/持久类只是因为其他东西没有释放而存在。

似乎是永远重复的操作导致了我的泄漏。我所要做的就是循环遍历场景中的所有节点并删除它们的 Action 。

这个问题帮助了我

iOS 7 Sprite Kit freeing up memory

更新:我最近再次重新讨论了这个主题,因为我觉得手动删除节点上的操作不是必要的,而且可能会变得很麻烦。经过更多研究,我找到了(我的)内存泄漏的确切问题。

像这样的“SKAction 永远重复”显然会导致泄漏。

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

所以需要改成这样才不会造成泄漏

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

这直接来 self 制作的 Apple 错误报告。

关于Swift SpriteKit ARC 傻瓜版,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35798496/

相关文章:

c++ - 为什么或在什么情况下,您会在 C++ 中将参数作为引用(或指针)传递给函数?

ios - 如何在 SpriteKit SKScene 类中启动 ReplayKit 屏幕录制

ios - 使用 map 边界和固定 y 位置将相机约束在角色上 - SpriteKit

arrays - 检查数组的所有元素是否在 Swift 中具有相同的值

C:通过引用传递结构

ios - UITableView 中的搜索栏用于 Realm 对象

visual-studio - 项目引用与 DLL 引用 - 哪个更好?

interface-builder - 从 Storyboard打开 SKScene

ios - Swift 中的 UITableViewCell 中的 UIScrollView

swift - 使用正则表达式查找和替换字符串