这里每天共享一个 iOS 的新知识,快来重视我吧

讲讲 swift 中 defer 的完成原理和运用场景

前言

在 Swift 中,defer 句子供给了一种便捷的方式来编写在当前效果域退出时履行的代码。这关于资源管理十分有用,比如封闭文件句柄、开释手动分配的内存等。今天来探讨一下 defer 的用法,并结合 Swift 官方源码来解析其完成原理。

defer 的用法

defer 句子让你能够在即将脱离当前效果域时履行一些整理作业。不论是由于 return 句子提前退出,仍是抛出了一个过错,亦或是简单地到达效果域的末尾,defer 块中的代码都将被履行。

下边是一些详细的运用场景:

1. 锁的获取与开释

在多线程编程中,锁是一种常见的同步机制。运用 defer 来开释锁能够确保即便在产生过错或提前回来的情况下,锁也能被正确开释。

func safeMethod() {
    lock.lock()
    defer {
        lock.unlock()
    }
    // 履行需求同步的代码
    // 假如这里有 return 或抛出反常,defer 仍然确保锁被开释
}

2. 数据库操作

在进行数据库操作时,通常需求在操作完毕后封闭数据库衔接或事务。运用 defer 能够确保这些整理作业在各种退出途径上都能被履行。

func updateDatabase() {
    let connection = database.connect()
    defer {
        connection.close()
    }
    // 履行数据库更新操作
    // 不管操作成功仍是失利,衔接都会被封闭
}

3. 文件处理

在处理文件时,确保文件在不再需求时被封闭是很重要的。defer 句子能够协助咱们到达这个意图。

func processFile(path: String) throws {
    let file = try FileHandle(forReadingFrom: URL(fileURLWithPath: path))
    defer {
        file.closeFile()
    }
    // 读取并处理文件内容
}

完成原理

要深化理解 defer 的作业原理,咱们需求检查 Swift 的源码。在 Swift 项意图 GitHub 库房中,有一个文件 Defer.h,其间界说了 defer 相关的完成细节。

#ifndef SWIFT_BASIC_DEFER_H
#define SWIFT_BASIC_DEFER_H
#include "llvm/ADT/ScopeExit.h"
namespace swift {
  namespace detail {
    struct DeferTask {};
    template<typename F>
    auto operator+(DeferTask, F &&fn) ->
        decltype(llvm::make_scope_exit(std::forward<F>(fn))) {
      return llvm::make_scope_exit(std::forward<F>(fn));
    }
  }
}
#define DEFER_CONCAT_IMPL(x, y) x##y
#define DEFER_MACRO_CONCAT(x, y) DEFER_CONCAT_IMPL(x, y)
#define SWIFT_DEFER                                                            
  auto DEFER_MACRO_CONCAT(defer_func, __COUNTER__) =                           
      ::swift::detail::DeferTask() + [&]()
#endif // SWIFT_BASIC_DEFER_H

这段代码是用 C++ 写的,但它揭示了 defer 的核心机制。简单解释一下上边的代码:

  • SWIFT_DEFER 宏首先经过 DEFER_MACRO_CONCAT 宏和 __COUNTER__ 宏生成一个仅有的变量名

  • __COUNTER__ 是编译器供给的计数器,确保每个 SWIFT_DEFER 实例的变量名都是仅有的

  • 然后,它创立了一个 swift::detail::DeferTask 的实例,并运用+操作符重载将其与一个 lambda 表达式相加。这个 lambda 表达式是在SWIFT_DEFER 宏后面的代码块中界说的,它是在效果域完毕时需求履行的代码。

  • 经过+操作符重载,回来了一个由 llvm::make_scope_exit 创立的目标,该目标会在其析构函数中履行 lambda 表达式。

  • 这个回来的目标被赋值给了一个局部变量,当这个局部变量的效果域完毕时(即脱离 SWIFT_DEFER 地点的效果域时),它的析构函数会自动被调用,从而履行之前界说的 lambda 表达式。

  • 每个 SWIFT_DEFER 实例化的目标都遵循 C++ 局部变量的生命周期规矩,最终声明的局部变量会首先被销毁(LIFO 后进先出)。

了解了以上完成原理,咱们来看一些特殊场景:

1. 一个效果域中有多个 defer 时

上边说了局部变量的销毁是 LIFO 后进先出的,所以当有多个 defer 时,最终一个会先履行:

func complexOperation() {
    defer { print("整理操作 1") }
    defer { print("整理操作 2") }
    print("履行某些操作")
}
// 输出:
// 履行某些操作
// 整理操作 2
// 整理操作 1

2. 在 for 循环中运用

在循环中运用 defer 时,每次迭代都会创立一个新的 defer 块。这意味着 defer 中的代码会在每次迭代完毕时履行,而不是在整个循环完毕后。

for item in collection {
    defer { print("处理 (item)") }
    if item.shouldSkip {
        continue
    }
    // 处理项
}

在这个比如中,即便 continue 句子跳过了某些迭代,每个 defer 也都会在其对应的迭代完毕时履行。

3. 在 defer 中能够用 return 吗?

答案是不能,假如在 defer 中运用 return 句子,编译器会直接报错 'return' cannot transfer control out of a defer statement,这是由于 defer 中的代码是在效果域完毕后履行的,也就是说,履行到 defer 的时候,函数已经 return 过了。

4. 在一个效果域下只写一个 defer 能够吗?

答案是能够,但没必要,由于假如效果域下没有其他代码,defer 下的代码会立即履行,因而 defer 也就失去了含义,硬要这么写的话编译器会给个正告,但不会报错:

func doSoming() {
    defer {
        print("doSoming")
    }
}

结论

defer 是 Swift 中一个十分有用的特性,它简化了资源管理和整理作业的代码编写。

在实际开发中,合理使用 defer 不只能够减少过错,还能使代码更加明晰和易于保护。除此之外 defer 仍是一个经常在面试中被问到的知识点,期望经过本篇文章能协助你更好地理解和运用 defer 句子。

这里每天共享一个 iOS 的新知识,快来重视我吧

本文同步自微信大众号 “iOS新知”,每天准时共享一个新知识,这里仅仅同步,想要及时学到就来重视我吧!