2017-01-15 1 views
0

Я пытался сбросить GameScene, создав новый дубликат GameScene, который работает. Однако проблема заключается в том, что сцена не освобождает, что определенно является проблемой, так как я профилировал свое приложение с помощью Allocations и увидел, что память «забивается» и «накапливается». Это мой код для моего GameViewController и мой GameScene:Swift 3 (SpriteKit): Reseting 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" не печатается, так что сцена не освобождает.

ИЗМЕНИТЬ С НОВЫМ НОМЕРА: Я исправил проблему, где сцена не DEALLOCATE представляя нулевую сцену перед обновлением сцены со сценой я на самом деле хочу, чтобы показать. Однако теперь экран остается пустым, даже если я представляю свою новую сцену позже. Вот как выглядит мой код сейчас, когда я хочу сбросить 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) переопределить дезинтезирование ваших пользовательских классов, 2) искать и находить, используете ли вы себя внутри закрытий и видите ли это какой-либо сильный ссылочный цикл (например, сохранение сцены в блоке и блок сохраняя сцену из-за сильного самопотребления) и 3) удаляйте части кода до тех пор, пока не будет вызван деанит сцены. Кроме того, вполне вероятно, что у вас много утечек ... Это будет непростая задача, но именно так вы пойдете, чтобы решить эту проблему. – Whirlwind

+0

Не поймите меня неправильно в отношении «новичка», но я бы сказал, что вы новичок в SpriteKit из-за вопроса, который вы опубликовали, поэтому я думаю, что методы, которые я предложил, были бы проще для вас, чтобы решить эту проблему. – Whirlwind

+0

Да, вы правы; Я начинающий SpriteKit, как мне заменить мои экземпляры использования self, есть ли способ избежать этого? –

ответ

1

О методе presentScene (SKScene?)

Когда вы вызываете этот метод, основанный на том, что вы передаете ему, разные вещи будут происходить ... Но сначала ... Перед сценой представлена по SKView, у вас есть новая сцена и экземпляр SKView. SKView экземпляр имеет свойство сцены, а сцена имеет свойство вида. Эти свойства устанавливаются соответственно и автоматически (сцена становится осведомленной о своем представлении, а представление становится известно о вновь созданной сцене), когда представлена ​​сцена.

Теперь, когда вы вызываете presentScene(nil), текущая сцена удаляется из представления, поэтому автоматически ее свойство представления становится nil (также свойство сцены на экземпляре SKView становится нулем).

Серый экран

Поскольку сцена была удалена из вида, вы видите серый экран now.That по умолчанию цвет зрения. Итак, в настоящее время нет сцены. И ты собираешься сказать, но как? Я сделал:

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 (ноль) не разрешит утечки

Вызывать этот метод, просто установить свойство сцены к нулю на SKView, который представляет его. Он не освободит сцену, если что-то ее сохранит. Скажите, что вы сделали немного глупости в своем GameViewController и сделали сильную ссылку на сцену, которая в настоящее время представлена ​​(я думаю, для этого не нужен код?), Поэтому сцена будет сохранена до тех пор, пока GameViewController не освободится ... Значит, когда вы это сделаете:

presentScene(nil) 

ничего не произойдет. сцена из deinit не будет вызвана. Помните, что этот метод просто установил свойство свойства nil на SKView, который представляет сцену, но если это не последняя сильная ссылка на сцену, находящуюся в данный момент, сцена не будет освобождена (так работает ARC).

Кроме того, у вас может быть несколько утечек. Это не сцена, которая течет. У вас могут быть утечки других объектов, например. объект A является свойством сцены, а объект B является свойством сцены. Объект A сильно указывает на объект B и наоборот. Это сильный ссылочный цикл. Теперь сцена освободится, но эти два объекта останутся в памяти.

Вот почему я предложил в комментариях переопределить deinit methos на пользовательских классах.

Как решить проблему утечки?

Я бы пропустить вдаваясь в подробности о том, что, поскольку это было бы просто скопировать/документацию наклеивая Apple, и есть большое объяснение how to solve/prevent leaks (Person & квартиры, например). Но дело в том, что используйте слабые/неопубликованные ссылки внутри классов, которые могут сохранять друг друга. То же самое касается закрытия. Определите списки захвата и используйте слабое/незанятое я в том случае, если сцена сохраняет блок, а блок сохраняет сцену.

Для тех, кого больше интересует метод presentScene(SKScene?), есть некоторые цитаты из Технической заметки, которые я связал ниже (интересно, почему такая информация не является частью документов для метода presentScene(SKScene?)).

Существует упоминание в Technical Note, который говорит:

SKView поддерживает кэш сцен, которые она представляет для исполнения причин

и

пропускание ноля аргумента SKView presentScene не вызывает сцену для освобождения, вместо этого, чтобы освободить сцену, вы должны освободить SKView , который представил его.