我离开游戏后,SpriteKit会保留记忆

时间:2017-04-11 17:18:42

标签: swift memory memory-management memory-leaks sprite-kit

所以我正在开发一个包含SpriteKit游戏的应用程序。我注意到的是,在我开始和关闭游戏后,内存不断增加,如下图所示:

enter image description here

我使用模态演示文稿从View Controller呈现游戏。所以问题是: 有没有办法从内存中完全删除场景?

我正在点击View Controller上的关闭按钮以关闭它。 scene.quitGame()方法基本上解除了所有节点,动作等的释放。在一个理想的世界中,我甚至不需要做任何这样的事情,因为会有一个完全摆脱SKScene的魔法命令。

@IBAction func quit(_ sender: UIButton) {
    scene.quitGame()
    scene.removeFromParent()
    self.removeFromParentViewController()
    self.dismiss(animated: true, completion: nil)
    scene = nil
}

编辑:我使用以下代码从viewDidLoad呈现场景:

scene = GameScene(size: view.bounds.size)
let skView = view as! SKView
skView.showsFPS = false
skView.showsNodeCount = false
skView.ignoresSiblingOrder = true
scene.scaleMode = .resizeFill
if let heroImage2 = heroImage2, let myImage = myImage, let monsterImage = monsterImage{
    scene.addPicture(img: heroImage2, myImage: myImage,monsterImage: monsterImage, bigMonsterImage: bigMonsterImage!)
}
skView.presentScene(scene)
scene.viewController = self

编辑2 - 我注释了对此方法的调用,其中包括重复永久动作,泄漏似乎消失了

   func createHerosAction(duration: Double = 5.0){
        run(SKAction.repeatForever(
            SKAction.sequence([
                SKAction.run(addHero),
                SKAction.wait(forDuration: duration)
                ])
        ), withKey:"createHeros")
    }



func addHero() {
    // Create sprite
    if(heroImage == nil){
        heroImage = UIImage(named: "hero")
    }

    let Texture = SKTexture(image: heroImage!)
    let hero = SKSpriteNode(texture:Texture)
    hero.size.width = size.width * 0.17
    hero.size.height = hero.size.width
    hero.name = "hero"
    hero.zPosition = 2

    let heroSide = arc4random_uniform(2)

    hero.physicsBody = SKPhysicsBody(rectangleOf: hero.size) // 1
    hero.physicsBody?.isDynamic = true // 2
    hero.physicsBody?.categoryBitMask = PhysicsCategory.Hero // 3
    hero.physicsBody?.contactTestBitMask = PhysicsCategory.Projectile // 4
    hero.physicsBody?.collisionBitMask = PhysicsCategory.None // 5

    let actualY = random(min: size.height*0.3 + hero.size.height/2 + size.height * 0.12, max: size.height - hero.size.height/2 - size.height * 0.15)

    let pointRightOfScreen = CGPoint(x: size.width + hero.size.width/2, y: actualY)
    let pointLeftOfScreen = CGPoint(x:  -hero.size.width/2, y: actualY)

    if(heroSide == 0){
        hero.position = pointRightOfScreen
    }else{
        hero.position = pointLeftOfScreen
    }

    addChild(hero)

    let actualDuration = random(min: CGFloat(2.0), max: CGFloat(5.0))

    var actionMove = SKAction()

    if(heroSide == 0){
        actionMove = SKAction.move(to: pointLeftOfScreen, duration: TimeInterval(actualDuration))
    }else{
        actionMove = SKAction.move(to: pointRightOfScreen, duration: TimeInterval(actualDuration))
    }

    let numberOfUpsAndDown = random(min: CGFloat(1.0), max: CGFloat(10.0))
    let variance = random(min: CGFloat(-70.0), max: CGFloat(70.0))


    let mvHero = moveHeroUpAndDown(node: hero, variance: variance,duration: actualDuration/numberOfUpsAndDown)

    let actionMoveDone = SKAction.removeFromParent()


    let loseLifeAction = loseLife(node: hero)

    hero.run(SKAction.sequence([actionMove, loseLifeAction, actionMoveDone]), withKey: "moveHero")
    hero.run(mvHero, withKey: "UpAndDown")


}

编辑3:泄漏似乎是在上面调用的SKAction上。在我删除此操作后,泄漏停止了。

   func loseLife(node: SKSpriteNode) -> SKAction{

    let loseALifeAction = SKAction.run(){

        if(self.gamePaused == false){
            self.playSound(fileName: "losesound.wav")

            if(self.lives > 0){
                self.updateLives(newValue: self.lives - 1, lostLife: true)
                //self.lives -= 1
            }

            print("lives: \(self.lives)")

            if(self.lives <= 0){
                print("hit me")
                node.run(self.loseGame())
            }
        }
    }
    return loseALifeAction
}

2 个答案:

答案 0 :(得分:5)

根据您的评论,您应该使用capture lists

func createHerosAction(duration: Double = 5.0){

        run(SKAction.repeatForever(
            SKAction.sequence([

                SKAction.run {[unowned self] in
                    self.addHero()
                },
                SKAction.wait(forDuration: duration)
                ])
        ), withKey:"createHeros")
    }

这是一个很大的话题,但我建议你开始阅读我发布的链接,因为它已经详细解释了从&#34; ARC如何工作&#34;,&#34;什么是捕获列表& #34;对弱者和无主义的关键词。我已经写了好几次关于这个,以及这个网站上的许多其他人,这是一个很大的话题,所以这次我会跳过:)

答案 1 :(得分:0)

我相信它的内部SpriteKit泄漏将ViewController保存在内存中。我们发现SpriteKit在解除了viewController之后似乎保留了Scene和SKView。

在我们的案例中,我们开始专门在比赛结束后呈现一个零场景,就在我们通过屏幕呈现游戏之后。

    // Remove the game scene to stop game code and free up memory
    if let skView = view as? SKView {
        skView.presentScene(nil)
    }