Swift

本文详细介绍了 Swift 中的宏,包括独立宏和附加宏的区别、宏的声明与实现,以及如何利用宏在编译时生成代码。

Swift 宏(Macros)允许你在编译时生成代码,从而减少重复代码并增强代码的表现力。宏会在编译源代码之前进行转换,将宏调用展开为生成的代码。

宏的分类

Swift 中的宏主要分为两类:

  • 独立宏 (Freestanding Macros):独立出现,不依附于任何声明。
    • # 开头调用。
    • 例如:#warning("This is a warning")
  • 附加宏 (Attached Macros):修改或修饰与其附加的声明。
    • @ 开头调用。
    • 例如:@Observable

独立宏 (Freestanding Macros)

独立宏可以产生一个值,或者在编译时执行某些操作(如发出警告)。

func myFunction() {
    print("Currently running \(#function)")
    #warning("Something's wrong")
}

附加宏 (Attached Macros)

附加宏为声明添加额外的功能。例如,@OptionSet 宏可以自动为结构体生成遵循 OptionSet 协议所需的代码。

@OptionSet
struct SundaeToppings {
    private enum Options: Int {
        case nuts
        case cherry
        case fudge
    }
}

展开后,SundaeToppings 结构体将自动包含 rawValue 初始化器、static var 属性等。

宏声明

宏的声明定义了宏的名称、参数以及它在何处可以使用。

@freestanding(expression)
public macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "MyMacros", type: "StringifyMacro")
  • @freestanding(expression):指定这是一个表达式类型的独立宏。
  • #externalMacro:指定实现该宏的外部模块和类型。

宏实现

宏的实现通常在单独的 Swift Package 中,依赖 SwiftSyntax 库来操作抽象语法树(AST)。

public struct StringifyMacro: ExpressionMacro {
    public static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) -> ExprSyntax {
        guard let argument = node.argumentList.first?.expression else {
            fatalError("compiler bug: the macro does not have any arguments")
        }

        return "(\(argument), \(literal: argument.description))"
    }
}

总结

Swift 宏通过在编译时生成代码,极大地提高了代码的灵活性和可维护性。无论是消除样板代码,还是构建强大的领域特定语言(DSL),宏都是一个强有力的工具。

在 GitHub 上编辑

上次更新于