问题历史:
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仅允许在函数、方法、初始化器和析构函数内部使用。它不能放置在例如文件的全局空间或函数外的代码源中。
如果块中出现异常(throw),defer会发生什么?
defer仍然会执行。这是它的一个优点——即使在出现错误(throw)的情况下,资源也会被保证释放。这实现了安全释放资源的模式。
多个defer的执行顺序是什么?
它们按反向的顺序执行(LIFO):稍后声明的defer会优先执行。
代码示例:
func test() { defer { print("第一个") } defer { print("第二个") } print("内部") } test() // 输出: "内部", "第二个", "第一个"
具有多个、位置不连续的defer的函数;忘记清理某些资源,从函数的不同位置返回。这会导致资源泄漏和调试行为困难。
优点: 代码统一性,清理操作“集中”在一个地方。
缺点: 难以跟踪将执行什么以及顺序;逻辑错误可能导致泄漏。
为每个重要阶段制作一个defer,放置在资源初始化的地方,并添加注释。进行代码复审。
优点: 保证资源的清理,明确的操作顺序。
缺点: 在添加新资源和清理块时需要纪律和注意力。