Swift 3(SpriteKit):重置GameScene并不会解除分配

时间:2017-01-15 01:01:56

标签: ios swift sprite-kit skscene

我一直试图通过创建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)

任何人都知道如何解决这个问题?

感谢您的回答和帮助。

1 个答案:

答案 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.viewnil。而且因为你使用的是可选链接,所以一切都会优雅地失败。如果您更改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   提出来了。