我一直试图通过创建GameScene的新副本来重置GameScene,它可以正常工作。然而,问题在于场景没有解除分配,这绝对是一个问题,因为我用Allocations分析了我的应用程序,并发现内存已经堵塞了#39;并且打桩'起来。这是我的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)
}
&#34; GameScene deinited&#34;永远不会打印,所以场景永远不会解除分配。
使用新问题进行编辑: 我已经解决了在使用我真正想要的场景更新场景之前,通过呈现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)
任何人都知道如何解决这个问题?
感谢您的回答和帮助。
答案 0 :(得分:1)
关于presentScene(SKScene?)方法
当你调用这个方法时,基于你传递给它的东西,会发生不同的事情......但首先要做的事情......在场景由SKView
呈现之前,你有一个新创建的场景和SKView
实例。 SKView
实例具有场景属性,场景具有视图属性。当场景出现时,这些属性会相应地自动设置(场景会意识到它的视图,并且视图会发现新创建的场景)。
现在,当您调用presentScene(nil)
时,当前场景将从视图中删除,因此其视图属性自动变为nil(SKView
实例上的场景属性也变为零)。
灰色屏幕
因为场景已从视图中删除,所以您现在看到灰色屏幕。这是视图的默认颜色。所以,目前没有场景。你会说,但是怎么样?我做了:
self.view?.presentScene(newScene, transition: animation)
因为optional chaining,代码没有崩溃。一切都优雅失败(线未执行)。但现在你要说:
“等等,我见过去了!”
这是真的。但您所看到的是对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
解除分配。 .Means,当你这样做时:
presentScene(nil)
什么都不会发生。场景的deinit
将不会被调用。请记住,此方法只是在显示场景的SKView
上将场景属性设置为nil,但如果这不是对当前处置场景的最后一个强引用,则场景将不会被释放(这就是ARC的工作方式)。
此外,您可能会有多次泄漏。这不是泄漏的场景。您可以让其他对象泄漏,例如。对象A是场景的属性,对象B是场景的属性。对象A强烈指向对象B,反之亦然。那是强大的参考周期。现在场景将解除分配,但这两个对象将保留在内存中。
这就是为什么我在评论中建议覆盖自定义类的deinit方法。
如何解决泄漏?
我会跳过详细介绍,因为那只是复制/粘贴Apple的文档,并且how to solve/prevent leaks(人和公寓示例)有很好的解释。但重点是,在可能保留彼此的类中使用弱/无主引用。关闭也是如此。在场景保留块的情况下定义捕获列表并使用弱/无主自我,并且块保留场景。
对于那些对presentScene(SKScene?)
方法感兴趣的人,我在下面链接的技术说明中有一些引用(我想知道为什么这样的信息不是presentScene(SKScene?)
方法的文档的一部分)
Technical Note中有人提到:
SKView维护其呈现的场景缓存以提高性能 原因
和
将零参数传递给SKView presentScene不会导致场景 要释放,相反,要释放场景,你必须释放SKView 提出来了。