Swift与SpriteKit中sceneDidLoad多次调用问题求助
解决SKScene切换后方法重复调用的问题
看起来你遇到了SpriteKit开发中很常见的一个坑——场景切换后旧实例没有被正确释放,导致多次触发相同的方法逻辑。我来帮你拆解问题并给出具体解决方案:
问题根源
每次切换场景时,你通过fileNamed创建了新的场景实例,但旧场景因为强引用(比如未移除的通知观察者、闭包中的强引用、未清理的动作监听)没有被ARC自动回收。这些残留的旧场景实例依然会响应系统回调(比如update(_:)、didMove(to:)),所以第二次切换后有2个实例在运行,第三次3个,以此类推。
具体解决方案
1. 在场景离开时强制清理资源
重写场景的willMove(from:)方法,在场景即将被切换走的时候,清理所有可能导致强引用的资源:
class MenuScene: SKScene { override func willMove(from view: SKView) { // 移除所有通知观察者 NotificationCenter.default.removeObserver(self) // 停止所有正在运行的动作 self.removeAllActions() // 移除所有子节点(如果不需要保留状态的话) self.removeAllChildren() // 如果用了KVO监听,这里也要移除对应的观察者 // self.removeObserver(self, forKeyPath: "someProperty") } } class GamePlayScene: SKScene { override func willMove(from view: SKView) { // 同样的清理逻辑 NotificationCenter.default.removeObserver(self) self.removeAllActions() self.removeAllChildren() } }
2. 排查并解除强引用
检查你的场景代码中是否存在闭包强引用self的情况,比如异步回调、SKAction的completion块:
// ❌ 错误写法:闭包强引用self,导致场景无法释放 let action = SKAction.run { self.scoreLabel.text = "\(self.score)" } // ✅ 正确写法:使用weak self避免循环引用 let action = SKAction.run { [weak self] in guard let self = self else { return } self.scoreLabel.text = "\(self.score)" }
3. 验证场景是否被正确释放
给你的场景类添加deinit方法,确认切换后旧场景是否被回收:
class MenuScene: SKScene { deinit { print("MenuScene has been deinitialized") } } class GamePlayScene: SKScene { deinit { print("GamePlayScene has been deinitialized") } }
如果切换场景后控制台没有打印这些信息,说明还有强引用没解除,继续排查代码中的引用关系(比如是否有全局变量持有场景实例、代理是否用了强引用等)。
4. 优化场景切换方法(可选)
你的扩展方法本身没问题,但可以加上过渡动画(如果需要),并且确保新场景的scaleMode设置正确:
extension SKScene { func presentSceneWith(name: String) { guard let scene = SKScene(fileNamed: name) else { print("Failed to load scene: \(name)") return } scene.scaleMode = .aspectFill // 可以添加过渡动画,比如淡入淡出 let transition = SKTransition.fade(withDuration: 0.3) self.view?.presentScene(scene, transition: transition) } }
总结
只要确保旧场景能被ARC正常回收,就不会出现方法重复调用的问题。核心就是清理资源+解除强引用,通过willMove(from:)做统一清理,再配合deinit验证回收情况,很快就能解决这个问题。
内容的提问来源于stack exchange,提问作者mazen
相关产品推荐
相关产品推荐

