swift – SpriteKit没有释放所有使用过的内存
我已经准备好很多(如果不是全部)SO和其他网站上有关处理SpriteKit和内存问题的灾难的文章.像许多其他人一样,我的问题是在我离开我的SpriteKit场景之后几乎没有在场景会话期间添加的任何内存被释放.我试图在我发现的文章中实现所有建议的解决方案,包括但不限于……
1)确认在SKScene类中调用deinit方法. 2)确认场景类中没有对父VC的强引用. 3)强制删除所有子项和动作,并在VC消失时将场景设置为nil. (将场景设置为nil是最终被调用的deinit方法) 然而,在所有这些之后,内存仍然存在.一些背景,这个应用程序介于标准的UIKit视图控制器和SpriteKit场景之间(它是一个专业的绘图应用程序).例如,在进入SpriteKit场景之前,应用程序使用大约400 MB.进入场景并创建多个节点后,内存增长到1 GB以上(到目前为止都很好).当我离开现场时,内存可能下降100 MB.如果我重新进入现场,它会继续堆积.有关如何完全释放SpriteKit会话期间使用的所有内存的方法或建议吗?下面是一些用于尝试解决此问题的方法. SKScene课程 func cleanScene() { if let s = self.view?.scene { NotificationCenter.default.removeObserver(self) self.children .forEach { $0.removeAllActions() $0.removeAllChildren() $0.removeFromParent() } s.removeAllActions() s.removeAllChildren() s.removeFromParent() } } override func willMove(from view: SKView) { cleanScene() self.removeAllActions() self.removeAllChildren() } 介绍VC var scene: DrawingScene? override func viewDidLoad(){ let skView = self.view as! SKView skView.ignoresSiblingOrder = true scene = DrawingScene(size: skView.frame.size) scene?.scaleMode = .aspectFill scene?.backgroundColor = UIColor.white drawingNameLabel.text = self.currentDrawing?.name! scene?.currentDrawing = self.currentDrawing! scene?.drawingViewManager = self skView.presentScene(scene) } override func viewDidDisappear(_ animated: Bool) { if let view = self.view as? SKView{ self.scene = nil //This is the line that actually got the scene to call denit. view.presentScene(nil) } }
正如评论中所讨论的,问题可能与强大的参考周期有关.
下一步 >重新创建一个简单的游戏,其中场景被正确释放,但有些节点没有. 1.让我们创建一个带内存问题的游戏 让我们用SpriteKit创建一个基于Xcode的新游戏. 我们需要使用以下内容创建一个新文件Enemy.swift import SpriteKit class Enemy: SKNode { private let data = Array(0...1_000_000) // just to make the node more memory consuming var friend: Enemy? override init() { super.init() print("Enemy init") } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } deinit { print("Enemy deinit") } } 我们还需要使用以下源代码替换Scene.swift的内容 import SpriteKit class GameScene: SKScene { override init(size: CGSize) { super.init(size: size) print("Scene init") } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) print("Scene init") } override func didMove(to view: SKView) { let enemy0 = Enemy() let enemy1 = Enemy() addChild(enemy0) addChild(enemy1) } override func touchesBegan(_ touches: Set<UITouch>,with event: UIEvent?) { let newScene = GameScene(size: self.size) self.view?.presentScene(newScene) } deinit { print("Scene deinit") } }
让我们开始游戏并看一下控制台.等着瞧 场景初始化 这意味着我们总共有3个节点. 现在让我们点击屏幕,让我们再次看一下控制台 场景初始化 我们可以看到已经创建了一个新场景和2个新敌人(第4,5,6行).最后,旧场景被解除分配(第7行),并且2个旧敌人被解除分配(第8和第9行). 所以我们内存中仍有3个节点.这很好,我们没有记忆韭菜. 如果我们使用Xcode监视内存消耗,我们可以验证每次重新启动场景时内存需求没有增加. 2.让我们创建一个强大的参考周期 我们可以在Scene.swift中更新didMove方法,如下所示 override func didMove(to view: SKView) { let enemy0 = Enemy() let enemy1 = Enemy() // ?????? this is a scary strong retain cycle ?????? enemy0.friend = enemy1 enemy1.friend = enemy0 // ************************************************** addChild(enemy0) addChild(enemy1) } 如你所见,我们现在在敌人0和敌人1之间有一个强大的循环. 让我们再次运行游戏. 如果现在我们点击屏幕并看看我们将看到的控制台 场景初始化
我们来看看Xcode Memory Report 现在,每当我们用新的场景替换旧场景时,内存消耗就会增加. 3.找到仪器的问题 当然我们确切地知道问题的确切位置(我们在1分钟前添加了强保留周期).但是,我们怎样才能在一个大项目中发现强大的保留周期呢? 让我们点击Xcode中的乐器按钮(当游戏进入模拟器时). 然后在下一个对话框中单击“传输”. 现在我们需要选择泄漏检查
让我们让泄漏发生 回到模拟器并再次点击.场景将再次被替换. 这是我们的泄漏. 让我们扩展它. 仪器正在告诉我们8个敌人类型的物体已被泄露. 我们也可以选择视图Cycles和Root,Instrument会告诉我们这个 这是我们强大的保留周期!
5.解决问题 现在我们知道问题是Enemy类,我们可以回到我们的项目并解决问题. 我们可以简单地让朋友财产变弱. 让我们更新敌人类. class Enemy: SKNode { private let data = Array(0...1_000_000) weak var friend: Enemy? ... 我们可以再次检查以确认问题已经消失. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |