编程Swift开发者

在Swift中,defer是什么,它的用途是什么,以及使用时会遇到哪些潜在问题?

用 Hintsage AI 助手通过面试

回答。

问题历史:

defer在Swift中引入,以更方便地管理资源并在代码块结束后执行操作,类似于其他语言中的finally。这种结构有助于明确显示清理或完成资源操作的阶段。

问题:

许多初学者认为defer会立即执行嵌套代码,常常误解触发时机。另外,当有多个defer块时,可能会难以理解,如果不知道LIFO(后进先出)规则。try/catch和提前退出也可能会造成困难。

解决方案:

defer在作用域的最后执行嵌套代码,在当前函数块退出之前,即使函数是通过return、throw、break等提前退出的。多个defer的执行顺序是反向的。

代码示例:

func readFile() { print("打开文件") defer { print("关闭文件") } print("读取第1行") if Bool.random() { print("提前返回!") return } print("读取第2行") } readFile() // 控制台始终会显示:打开文件, ..., 关闭文件(在最后)

关键特性:

  • defer在函数/方法作用域退出时始终触发
  • 多个defer按栈的方式工作(LIFO)
  • 即使在提前返回或错误时也会触发

有陷阱的问题。

可以在函数外使用defer吗?

不可以,defer仅允许在函数、方法、初始化器和析构函数内部使用。它不能放置在例如文件的全局空间或函数外的代码源中。

如果块中出现异常(throw),defer会发生什么?

defer仍然会执行。这是它的一个优点——即使在出现错误(throw)的情况下,资源也会被保证释放。这实现了安全释放资源的模式。

多个defer的执行顺序是什么?

它们按反向的顺序执行(LIFO):稍后声明的defer会优先执行。

代码示例:

func test() { defer { print("第一个") } defer { print("第二个") } print("内部") } test() // 输出: "内部", "第二个", "第一个"

常见错误和反模式

  • 在可能出现自定义的return/throw的代码中使用defer,导致执行顺序混乱
  • 滥用defer,使函数中许多块的可读性变差
  • 尝试在函数的作用域外使用defer

生活中的示例

负面案例

具有多个、位置不连续的defer的函数;忘记清理某些资源,从函数的不同位置返回。这会导致资源泄漏和调试行为困难。

优点: 代码统一性,清理操作“集中”在一个地方。

缺点: 难以跟踪将执行什么以及顺序;逻辑错误可能导致泄漏。

正面案例

为每个重要阶段制作一个defer,放置在资源初始化的地方,并添加注释。进行代码复审。

优点: 保证资源的清理,明确的操作顺序。

缺点: 在添加新资源和清理块时需要纪律和注意力。