在编辑器播放模式停止时,哪些回调会跳过

解读

国内面试里,这道题常被用来快速区分“只会写业务脚本”与“真正理解Unity生命周期”的候选人
面试官真正想听的是:

  1. 你能把编辑器专属回调运行时回调分清楚;
  2. 你知道Play Mode 退出瞬间引擎为了提速会故意砍掉哪些通知
  3. 你能给出可验证的测试方法,而不是背文档。

知识点

  1. 编辑器回调

    • OnValidate:只在Inspector 值改变时触发,退出Play Mode不会执行
    • OnDrawGizmos / OnDrawGizmosSelected仅渲染辅助图示,退出时直接砍掉,不调用
    • Reset:只在首次添加脚本或Reset 按钮时触发,与播放状态无关,退出时更不会补一次。
  2. 运行时回调

    • OnDestroyOnDisable正常执行,但顺序不保证(尤其DontDestroyOnLoad对象)。
    • OnApplicationQuit:在真机/PC 包里必调;编辑器里若用户点击Stop直接终止,不会执行
    • OnApplicationPauseOnApplicationFocus:编辑器下模拟事件不可靠,退出Play Mode时直接跳过
  3. 特殊陷阱

    • 协程yield return new WaitForSeconds(0.01f) 这类未完成的协程在退出时直接销毁不会触发finally
    • 异步操作UnityWebRequestAssetBundle.UnloadAsync后台任务在退出时立即中断回调不会回来
    • ScriptableObject没有场景绑定,退出时不调用OnDestroy只等GC;很多候选人误以为会走。
  4. 验证手段(面试加分项)
    InitializeOnLoad里给EditorApplication.playModeStateChanged加监听,打印日志即可肉眼确认哪些回调被跳过。

答案

在编辑器点击Stop 按钮退出Play Mode时,以下回调会被跳过(即不会执行):

  • OnValidate
  • OnDrawGizmos / OnDrawGizmosSelected
  • Reset
  • OnApplicationQuit(编辑器模式特有跳过)
  • OnApplicationPause / OnApplicationFocus
  • 未完成的协程后续段
  • 异步操作的完成回调(UnityWebRequest、AssetBundleRequest 等)

OnDestroy、OnDisable仍会被调用,但顺序不可依赖ScriptableObject 的OnDestroy在编辑器下不会触发

拓展思考

  1. 真机包与编辑器差异
    在iOS/Android 真机上,OnApplicationQuit同样不会调用(系统直接杀进程),但OnApplicationPause(true)会作为“进入后台”信号,可用于存档;面试时可主动对比,体现平台差异意识

  2. 资源泄漏排查
    由于异步回调被跳过AssetBundle.Unload(false)可能在编辑器里看似成功,实际native 内存未释放;建议自定义PlayModeExitCleaner,在playModeStateChanged强制回收,避免**“编辑器不泄漏,真机爆内存”**的悲剧。

  3. 热更新框架兼容
    国内主流xlua/ILRuntime 热更方案,常在OnDestroy卸载DLL;若业务把关键持久化逻辑写在OnApplicationQuit,编辑器下永远跑不到,导致本地调试通过,外网丢档;框架层应统一收口到OnDisable + 自定义事件不依赖被跳过的回调

  4. 面试反问技巧
    答完后可以反问面试官:“咱们项目是否用Addressables?退出Play Mode时AsyncOperationHandle.Completed也会被砍,我曾用Custom Yield Instruction强制同步卸载,您看这种方案在咱们框架里是否合适?”——把问题抛回去,展示实战深度