Skip to Content

延后执行(Defer)

defer 把一条语句注册成「离开当前块之前必须执行」的清理钩子。注册时不执行,执行时倒序。资源有 Dispose 协议的走 using,其余收尾场景走 defer。

基本形态

多个 defer 按声明的逆序执行,后注册的先跑:

defer { print("最先声明的 defer 最后跑"); } defer { print("最后声明的 defer 先跑"); } print("D");
D 最后声明的 defer 先跑 最先声明的 defer 最后跑

逆序保证「后开的先关」,与资源获取的嵌套顺序一致。

块作用域

defer 挂在它所在的块上,不是挂在整个函数上。内层块结束,内层的 defer 就执行:

print("A"); { defer { print("内块 defer"); } print("B"); } print("C");
A B 内块 defer C

与返回值的关系

defer 定位是清理钩子,返回值表达式在 return 处求值,defer 对求值结果不可见:

func Expression() -> int { var n = 1; defer { n = 99; } return n + 0; }

n + 0 在 return 时已经算成 1,defer 再改 n 不影响它。

体内禁用语句

defer 体内不得使用 return、break、continue、fail,写了报 MS4027:

error MS4027: 'return' must not be used inside a defer body.

清理钩子自己再跳转,执行顺序就没法推理了,语言层面直接禁止。

注意

  • defer 语句 接单条语句,最常见写法是 defer { 块 }。
  • 凡离开注册了 defer 的块都会触发:return、fail(包括失败从被调函数传播穿过本帧)或块正常结束,defer 都在该块退出前跑。main 顶层带错退出前,main 里的 defer 同样先跑。唯一不跑的路径是进程级崩溃(crash),终态不执行任何清理。
  • defer 注册在哪个块,就在那个块退出时跑:内块的 defer 不等函数退出。
  • defer 里的 out 参数在 fail 路径不回写,失败路径的输出参数不可依赖。
最后更新于 2026年10月8日