在编辑器播放模式停止时,哪些回调会跳过
解读
国内面试里,这道题常被用来快速区分“只会写业务脚本”与“真正理解Unity生命周期”的候选人。
面试官真正想听的是:
- 你能把编辑器专属回调与运行时回调分清楚;
- 你知道Play Mode 退出瞬间引擎为了提速会故意砍掉哪些通知;
- 你能给出可验证的测试方法,而不是背文档。
知识点
-
编辑器回调
OnValidate:只在Inspector 值改变时触发,退出Play Mode不会执行。OnDrawGizmos/OnDrawGizmosSelected:仅渲染辅助图示,退出时直接砍掉,不调用。Reset:只在首次添加脚本或Reset 按钮时触发,与播放状态无关,退出时更不会补一次。
-
运行时回调
OnDestroy、OnDisable:正常执行,但顺序不保证(尤其DontDestroyOnLoad对象)。OnApplicationQuit:在真机/PC 包里必调;编辑器里若用户点击Stop,直接终止,不会执行。OnApplicationPause、OnApplicationFocus:编辑器下模拟事件不可靠,退出Play Mode时直接跳过。
-
特殊陷阱
- 协程:
yield return new WaitForSeconds(0.01f)这类未完成的协程在退出时直接销毁,不会触发finally块。 - 异步操作:
UnityWebRequest、AssetBundle.UnloadAsync等后台任务在退出时立即中断,回调不会回来。 - ScriptableObject:没有场景绑定,退出时不调用
OnDestroy,只等GC;很多候选人误以为会走。
- 协程:
-
验证手段(面试加分项)
在InitializeOnLoad里给EditorApplication.playModeStateChanged加监听,打印日志即可肉眼确认哪些回调被跳过。
答案
在编辑器点击Stop 按钮退出Play Mode时,以下回调会被跳过(即不会执行):
- OnValidate
- OnDrawGizmos / OnDrawGizmosSelected
- Reset
- OnApplicationQuit(编辑器模式特有跳过)
- OnApplicationPause / OnApplicationFocus
- 未完成的协程后续段
- 异步操作的完成回调(UnityWebRequest、AssetBundleRequest 等)
而OnDestroy、OnDisable仍会被调用,但顺序不可依赖;ScriptableObject 的OnDestroy在编辑器下不会触发。
拓展思考
-
真机包与编辑器差异:
在iOS/Android 真机上,OnApplicationQuit同样不会调用(系统直接杀进程),但OnApplicationPause(true)会作为“进入后台”信号,可用于存档;面试时可主动对比,体现平台差异意识。 -
资源泄漏排查:
由于异步回调被跳过,AssetBundle.Unload(false)可能在编辑器里看似成功,实际native 内存未释放;建议自定义PlayModeExitCleaner,在playModeStateChanged里强制回收,避免**“编辑器不泄漏,真机爆内存”**的悲剧。 -
热更新框架兼容:
国内主流xlua/ILRuntime 热更方案,常在OnDestroy里卸载DLL;若业务把关键持久化逻辑写在OnApplicationQuit,编辑器下永远跑不到,导致本地调试通过,外网丢档;框架层应统一收口到OnDisable + 自定义事件,不依赖被跳过的回调。 -
面试反问技巧:
答完后可以反问面试官:“咱们项目是否用Addressables?退出Play Mode时AsyncOperationHandle.Completed也会被砍,我曾用Custom Yield Instruction做强制同步卸载,您看这种方案在咱们框架里是否合适?”——把问题抛回去,展示实战深度。