You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:16:10