我一直在尝试通过创建 GameScene 的新副本来重置 GameScene,它可以工作。然而,问题是场景没有解除分配,这绝对是一个问题,因为我使用分配分析我的应用程序并看到内存“阻塞”和“堆积”。这是我的 GameViewController 和 GameScene 的代码:
import UIKit
import SpriteKit
import GameplayKit
var screenSize = CGSize()
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
screenSize = scene.size
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
那么我的GameScene基本上是:
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
//Declare and initialise variables and enumerations here
deinit{print("GameScene deinited")}
override func didMove(to view: SKView) {
//Setup scene and nodes
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//Do other things depending on when and where you touch
//When I want to reset the GameScene
let newScene = GameScene(size: self.size)
newScene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
newScene.scaleMode = self.scaleMode
let animation = SKTransition.fade(withDuration: 1.0)
self.view?.presentScene(newScene, transition: animation)
}
“GameScene deinited”永远不会被打印出来,所以场景永远不会被释放。
编辑新问题:我已经通过在使用我实际想要显示的场景更新场景之前呈现一个 nil 场景来解决场景没有解除分配的问题。但是,即使我之后展示了我的新场景,现在屏幕仍然保持空白。当我想重置 GameScene 时,这是我的代码现在的样子:
self.view?.presentScene(nil)
let newScene = GameScene(size: self.size)
newScene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
newScene.scaleMode = self.scaleMode
let animation = SKTransition.fade(withDuration: 1.0)
self.view?.presentScene(newScene, transition: animation)
有谁知道如何解决这个问题?
感谢您的任何回答和帮助。
最佳答案
关于presentScene(SKScene?)方法
当您调用此方法时,根据您传递给它的内容,会发生不同的事情...但首先要做的事情...在场景由 SKView
呈现之前,您有一个新创建的场景和一个 SKView
实例。 SKView
实例具有场景属性,场景具有 View 属性。当呈现场景时,这些属性会相应地自动设置(场景会感知其 View ,而 View 会感知新创建的场景)。
现在当您调用 presentScene(nil)
,当前场景正在从 View 中删除,因此其 View 属性自动变为 nil(SKView
实例上的场景属性也变为 nil)。
灰屏
由于场景已从 View 中删除,您现在看到的是灰色屏幕。这是 View 的默认颜色。所以,目前没有场景。你会说,但是怎么做呢?我做了:
self.view?.presentScene(newScene, transition: animation)
好吧,代码并没有因为 optional chaining 而崩溃.一切都优雅地失败了(行未执行)。但现在你要说:
"Wait, I've seen deinit call!"
这是真的。但是你看到的是一个
deinit
调用newScene
实例。那是因为上面的行失败了,新的场景没有出现,当前的方法到了最后,因为没有东西保留这个新的实例,它释放了。尽管如此,当前场景实际上并没有被释放,因为如果是这样的话,你会看到 两个 deinit
来电。以下是如何检查哪个实例已被释放(检查内存地址):
deinit{
print("deinit : \(self) has address: \(Unmanaged.passUnretained(self).toOpaque())")
}
如果您打印出
self.view
在 self.presentScene(nil)
之后行,你会看到 self.view
是 nil
.而且因为您使用的是可选链接,所以一切都会优雅地失败。如果您更改 self.view!.presentScene(newScene)
你会因为要找到 nil
而大喊大叫。同时打开可选的。调用 presentScene(nil) 将无法解决泄漏
调用此方法,只需将
SKView
上的场景属性设置为 nil呈现它。如果有东西保留它,它不会释放场景。假设您在 GameViewController
中做了一些愚蠢的事情, 并强烈引用了当前呈现的场景(我猜这不需要代码?)所以场景将保留到 GameViewController
解除分配...意味着,当你这样做时:presentScene(nil)
什么都不会发生。现场的
deinit
不会被调用。请记住,此方法只是将 SKView
上的场景属性设置为 nil。呈现场景,但如果这不是对当前处理场景的最后一个强引用,则不会释放场景(这就是 ARC 的工作方式)。此外,您可能有多个泄漏。泄漏的不是现场。您可以让其他对象泄漏,例如。对象 A 是场景的属性,对象 B 是场景的属性。对象 A 强烈指向对象 B,反之亦然。那就是强引用循环。现在场景将释放,但这两个对象将保留在内存中。
这就是为什么我在评论中建议在自定义类上覆盖 deinit 方法。
如何解决泄漏?
我将跳过对此的详细说明,因为那只是复制/粘贴 Apple 的文档,并且对 how to solve/prevent leaks 有很好的解释。 (个人和公寓示例)。但关键是,在可能相互保留的类中使用弱/无主引用。闭包也是如此。定义捕获列表并在场景保留 block 的情况下使用弱/无主自我,并且 block 保留场景。
对于那些对
presentScene(SKScene?)
更感兴趣的人方法,我在下面链接了一些来自技术说明的引用(我想知道为什么这样的信息不是 presentScene(SKScene?)
方法的文档的一部分)。Technical Note 中有提及说的是:
SKView maintains a cache of the scenes it presents for performance reasons
和
Passing a nil argument to SKView presentScene does not cause the scene to be freed, instead, to release the scene, you must free the SKView that presented it.
关于ios - swift 3 (SpriteKit) : Reseting GameScene doesn't deallocate,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41656740/