Swift 傻瓜式 SpriteKit ARC
Swift SpriteKit ARC for dummies
我一直在努力思考强大的参考循环,但我很挣扎。我一直在阅读 apple 和某些网站上的文档,我觉得他们并没有真正解决我的问题。
我知道你必须使用 weak 和 unowned 取决于对象是否可以为 nil。所以说你必须像这样class 2
class Person {
var dog: Dog?
....
}
class Dog {
weak var person: Person?
}
相互引用我知道其中一个必须使用 weak/unowned。这是大多数教程中看到的 classic 示例。
我也见过这样的例子,它们对我来说也很有意义
class Person {
unowned let gameScene: GameScene
init(scene: GameScene) {
self.gameScene = scene
....
}
我也明白 NSTimers 如果不失效的话会导致强引用循环。我没有使用 NSTimers,所以这应该不是问题。
我还了解到协议也会导致内存泄漏,因此他们处理它们的方法是将其设为 class 协议
protocol TestDelegate: class { }
我试图引用协议的地方使它变弱 属性
class: SomeClass {
weak var myDelegate: TestDelegate?
}
最后我知道了我像这样捕获自我的闭包
SKAction.runBlock { [unowned self] in
self.player.runAction....
}
然而,当涉及到我的实际 spritekit 游戏时,我似乎到处都有引用循环,即使是简单的 classes 我肯定不会引用另一个 class,而且我不明白为什么。
所以我的主要问题是
1) 是所有属性都创建强引用循环,还是只创建在 class 初始化之前创建的全局属性?
2) 在我简单的 classes 中还有哪些其他东西可能会产生强大的引用循环?
例如,我正在使用 class 创建平台
class Platform: SKSpriteNode {
/// basic code to create platforms
/// some simple methods to rotate platforms, move them left or right with SKActions.
现在我有类似的 class 陷阱、敌人等。同样,它们通常只是设置精灵属性(物理体等),并有一些方法来设置它们的动画或旋转它们。它们没有什么特别的,特别是在引用其他 classes 或场景时。
在我的 gameScene 中,我有一个创建平台的方法(对于敌人、陷阱等也是一样)
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 个进入瞬态,当我更改到我的 menuScene 时,2 个保持持久状态。奇怪的是,总是只有 1 个被删除,不管我更改顺序还是 create/delete 某些平台。所以看起来他们正在创建强引用,除了 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)
}
}
同样的问题。我在我的场景中创建了一些标志,有时标志不会被删除,有时会。同样,这个 class 没有引用任何场景或自定义 class,它只是创建一个精灵并为其设置动画。
在我的场景中,我有一个全局 属性 标志
class GameScene: SKScene {
var flag: Flag!
func didMoveToView...
}
如果标志本身不引用 GameScene,为什么会创建强引用?。我也不能用
weak var flag: Flag!
因为一旦标志被初始化我就崩溃了。
这样做有什么明显的遗漏吗?
在 Instruments 中找到它们有什么好技巧吗,因为这对我来说似乎很疯狂。
这让我感到困惑,主要是因为我的 classes 非常简单并且没有引用其他自定义 classes、场景、viewControllers 等
是所有属性都创建强引用循环,还是只创建在 class 初始化之前创建的全局属性?
对对象的每个强引用都可能成为强引用循环的一部分。可能是
- 强储属性
- 强local/globalvariable/constant
With strong I mean it is not declared as weak or unowned.
事实上,如果在给定的时刻存在从对象到对象本身的强引用路径,则将创建循环并且 ARC 不会为循环中的所有对象释放内存。
在我简单的 classes 中还有哪些其他东西可能会产生强大的引用循环?
不用多说了。当对象 A
对对象 B
具有强引用时...对 A
具有强引用时,您就有了一个强引用循环。
几点建议:
跟踪取消初始化
只需向您的 class 添加一个反初始化器,并在控制台上检查您的对象是否在您期望的时候被反初始化
class Foo: SKSpriteNode {
deinit {
print(String(self))
}
}
让 SpriteKit 管理强引用
避免场景图中节点之间的强引用。请改用 SKNode
class 提供的方法和计算属性来检索其他节点,例如
.scene
.parent
.childNodeWithName(...)
.childrean
这种方法可能会导致一些性能问题,但您应该首先使代码正确,然后尝试提高性能。
感谢您的所有回答,尤其是 appzYourLift 和您的详细回复。
我整天都在研究我的 spriteKit 项目,也在 playground 上做了很多试验,我的游戏现在完全没有泄漏,没有找到强大的参考循环。
实际上,我在仪器中看到的很多 leaks/persistant 类 只是因为其他东西没有释放。
似乎是永远重复的动作导致了我的泄密。我所要做的就是遍历场景中的所有节点并删除它们的动作。
这个问题对我有帮助
iOS 7 Sprite Kit freeing up memory
更新:我最近再次访问了这个主题,因为我觉得不必手动删除节点上的操作是不必要的,而且可能会变得很麻烦。经过更多研究,我发现了(我的)内存泄漏的确切问题。
像这样的 "SKAction repeat forever" 显然会导致泄漏。
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))
这直接来自我所做的 Apple 错误报告。
我一直在努力思考强大的参考循环,但我很挣扎。我一直在阅读 apple 和某些网站上的文档,我觉得他们并没有真正解决我的问题。
我知道你必须使用 weak 和 unowned 取决于对象是否可以为 nil。所以说你必须像这样class 2
class Person {
var dog: Dog?
....
}
class Dog {
weak var person: Person?
}
相互引用我知道其中一个必须使用 weak/unowned。这是大多数教程中看到的 classic 示例。
我也见过这样的例子,它们对我来说也很有意义
class Person {
unowned let gameScene: GameScene
init(scene: GameScene) {
self.gameScene = scene
....
}
我也明白 NSTimers 如果不失效的话会导致强引用循环。我没有使用 NSTimers,所以这应该不是问题。
我还了解到协议也会导致内存泄漏,因此他们处理它们的方法是将其设为 class 协议
protocol TestDelegate: class { }
我试图引用协议的地方使它变弱 属性
class: SomeClass {
weak var myDelegate: TestDelegate?
}
最后我知道了我像这样捕获自我的闭包
SKAction.runBlock { [unowned self] in
self.player.runAction....
}
然而,当涉及到我的实际 spritekit 游戏时,我似乎到处都有引用循环,即使是简单的 classes 我肯定不会引用另一个 class,而且我不明白为什么。
所以我的主要问题是
1) 是所有属性都创建强引用循环,还是只创建在 class 初始化之前创建的全局属性?
2) 在我简单的 classes 中还有哪些其他东西可能会产生强大的引用循环?
例如,我正在使用 class 创建平台
class Platform: SKSpriteNode {
/// basic code to create platforms
/// some simple methods to rotate platforms, move them left or right with SKActions.
现在我有类似的 class 陷阱、敌人等。同样,它们通常只是设置精灵属性(物理体等),并有一些方法来设置它们的动画或旋转它们。它们没有什么特别的,特别是在引用其他 classes 或场景时。
在我的 gameScene 中,我有一个创建平台的方法(对于敌人、陷阱等也是一样)
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 个进入瞬态,当我更改到我的 menuScene 时,2 个保持持久状态。奇怪的是,总是只有 1 个被删除,不管我更改顺序还是 create/delete 某些平台。所以看起来他们正在创建强引用,除了 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)
}
}
同样的问题。我在我的场景中创建了一些标志,有时标志不会被删除,有时会。同样,这个 class 没有引用任何场景或自定义 class,它只是创建一个精灵并为其设置动画。
在我的场景中,我有一个全局 属性 标志
class GameScene: SKScene {
var flag: Flag!
func didMoveToView...
}
如果标志本身不引用 GameScene,为什么会创建强引用?。我也不能用
weak var flag: Flag!
因为一旦标志被初始化我就崩溃了。
这样做有什么明显的遗漏吗? 在 Instruments 中找到它们有什么好技巧吗,因为这对我来说似乎很疯狂。 这让我感到困惑,主要是因为我的 classes 非常简单并且没有引用其他自定义 classes、场景、viewControllers 等
是所有属性都创建强引用循环,还是只创建在 class 初始化之前创建的全局属性?
对对象的每个强引用都可能成为强引用循环的一部分。可能是
- 强储属性
- 强local/globalvariable/constant
With strong I mean it is not declared as weak or unowned.
事实上,如果在给定的时刻存在从对象到对象本身的强引用路径,则将创建循环并且 ARC 不会为循环中的所有对象释放内存。
在我简单的 classes 中还有哪些其他东西可能会产生强大的引用循环?
不用多说了。当对象 A
对对象 B
具有强引用时...对 A
具有强引用时,您就有了一个强引用循环。
几点建议:
跟踪取消初始化
只需向您的 class 添加一个反初始化器,并在控制台上检查您的对象是否在您期望的时候被反初始化
class Foo: SKSpriteNode {
deinit {
print(String(self))
}
}
让 SpriteKit 管理强引用
避免场景图中节点之间的强引用。请改用 SKNode
class 提供的方法和计算属性来检索其他节点,例如
.scene
.parent
.childNodeWithName(...)
.childrean
这种方法可能会导致一些性能问题,但您应该首先使代码正确,然后尝试提高性能。
感谢您的所有回答,尤其是 appzYourLift 和您的详细回复。
我整天都在研究我的 spriteKit 项目,也在 playground 上做了很多试验,我的游戏现在完全没有泄漏,没有找到强大的参考循环。
实际上,我在仪器中看到的很多 leaks/persistant 类 只是因为其他东西没有释放。
似乎是永远重复的动作导致了我的泄密。我所要做的就是遍历场景中的所有节点并删除它们的动作。
这个问题对我有帮助
iOS 7 Sprite Kit freeing up memory
更新:我最近再次访问了这个主题,因为我觉得不必手动删除节点上的操作是不必要的,而且可能会变得很麻烦。经过更多研究,我发现了(我的)内存泄漏的确切问题。
像这样的 "SKAction repeat forever" 显然会导致泄漏。
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))
这直接来自我所做的 Apple 错误报告。