这里每天共享一个 iOS 的新知识,快来重视我吧
前言
在 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新知”,每天准时共享一个新知识,这里仅仅同步,想要及时学到就来重视我吧!