Swift 5.9 Macros 有哪些新更新

这篇具有很好参考价值的文章主要介绍了Swift 5.9 Macros 有哪些新更新。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Swift 5.9 Macros 有哪些新更新,# Swift 集,swift,开发语言,蓝桥杯,ios,swiftui,原力计划

前言

虽然 Swift 6 已经在地平线上浮现,但 5.x 版本仍然有很多新功能-更简单的 if 和 switch 用法、宏、非可复制类型、自定义 actor 执行器等等都将在 Swift 5.9 中推出,再次带来了一个巨大的更新。

Macros(宏)

Macros(宏)在 Swift 中被引入,其中 SE-0382、SE-0389 和 SE-0397 结合起来,允许我们在编译时创建能够转换语法的代码。

在像 C++ 这样的语言中,宏是一种对代码进行预处理的方式,可以在代码被主编译器看到之前对其进行文本替换,从而生成那些你不想手动编写的代码。

Swift 的宏类似,但功能更强大,因此也更加复杂。还允许我们在编译前动态操作项目的 Swift 代码,从而在编译时注入额外的功能。

需要了解的关键信息

宏是安全的类型,不仅仅是简单的字符串替换,因此需要准确告诉宏它将处理的数据。

在构建阶段作为外部程序运行,并且不属于主应用目标。

宏被分解为多个较小的类型,例如 ExpressionMacro 用于生成单个表达式,AccessorMacro 用于添加 getter和setter,ConformanceMacro 用于使类型符合某个协议。

宏与解析后的源代码一起工作,可以查询代码的各个部分,例如正在操作的属性的名称、类型,或者结构体内部的各种属性。

宏在一个沙盒中工作,只能在给定的数据上操作。

最后一个部分最重要,Swift 的宏支持是基于 Apple 的 SwiftSyntax 库构建的,用于理解和操作源代码。必须将其作为宏的依赖项添加到项目中。

环境准备

了解完 Swift 宏的基本信息之后,终于我们可以进入到实战环节了,工欲善其事必先利其器,首先我们需要做好以下准备

  • macOS Ventura 13.3 以上操作系统
  • Xcode 15 以上,本文使用的版本是15.0 beta (15A5160n)
  • Swift 入门级语法(掌握 Hello World 的 4 种写法🙂)

然后我们需要初始化一个 Swift 宏开发工程。

1、直接打开 Xcode 15,File -> New -> Package

Swift 5.9 Macros 有哪些新更新,# Swift 集,swift,开发语言,蓝桥杯,ios,swiftui,原力计划

2、选择 Swift Macro 模版,然后给我们的工程起一个名字,我这里就叫做 “SwiftMacroKit”

Swift 5.9 Macros 有哪些新更新,# Swift 集,swift,开发语言,蓝桥杯,ios,swiftui,原力计划

3、打开工程,大功告成

这是一个 SPM 管理的工程,如果打开 Package 文件,我们可以看到它依赖了前面提到的 SwiftSyntax 库

dependencies: [
        // Depend on the latest Swift 5.9 prerelease of SwiftSyntax
        .package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0-swift-5.9-DEVELOPMENT-SNAPSHOT-2023-04-25-b"),
    ],

除此之外,工程其实还依赖了两个库, SwiftSyntaxBuilder 和 SwiftSyntaxMacros ,这三个库的职责分别是

  • SwiftSyntax:提供 Swift 语法树支持
  • SwiftSyntaxMacros:提供实现宏所需要的协议和类型
  • SwiftSyntaxBuilder:为展开新代码提供便捷的语法树创建 API

整个工程的源码主要有 4 个部分,截图示意如下

Swift 5.9 Macros 有哪些新更新,# Swift 集,swift,开发语言,蓝桥杯,ios,swiftui,原力计划

它们分别是

  • SwiftMacroKit:包含 Swift 宏的定义,注意这里不提供实现,但是会将定义与实现连接起来
  • SwiftMacroKitClient:一个测试工程(所以称为 Client),可以在 main 函数里测试 SwiftMacroKit 定义的宏
  • SwiftMacroKitMacros:宏的核心实现部分,最终打包成 macro 产物提供给其他模块使用,例如在 SwiftMacroKit 中引用
  • SwiftMacroKitTests:Swift 宏的测试模块,苹果官方推荐我们采用 TDD 的方式开发我们的宏,后面会讲到

创建一个宏

从一个简单的宏开始,以便可以看到它们的工作原理。

因为宏是在编译时运行的,所以我们可以创建一个小宏,返回应用程序构建的日期和时间——这对于调试诊断非常有帮助。

定义宏

首先,需要创建执行宏展开的代码,将 #buildDate 转换为类似于 “2023-06-05T18:00:00Z” 的字符串:

public struct BuildDateMacro: ExpressionMacro {
    public static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) -> ExprSyntax {
        let date = ISO8601DateFormatter().string(from: .now)
        return "\"\(raw: date)\""
    }
}

重要提示:这段代码不应该出现在主应用目标中,我们不希望这段代码编译到最终的应用程序中,只需要在其中插入生成的日期字符串。

在同一个模块中,创建一个符CompilerPlugi协议的结构体,导出宏:

import SwiftCompilerPlugin
import SwiftSyntaxMacros

@main
struct MyMacrosPlugin: CompilerPlugin {
    let providingMacros: [Macro.Type] = [
        BuildDateMacro.self
    ]
}

然后将其添加到 Package.swift 中的目标列表中:

.macro(
  name: "MyMacrosPlugin",
  dependencies: [
    .product(name: "SwiftSyntax", package: "swift-syntax"),
    .product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
    .product(name: "SwiftCompilerPlugin", package: "swift-syntax")
  ]
)

这样就完成了在外部模块中创建宏的过程。剩下的代码可以放在任何想要使用宏的地方,比如主应用目标中。

这需要两个步骤,首先是定义宏是什么。在例子中,这是一个自由表达式宏,将返回一个字符串,存在于 MyMacrosPlugin 模块中,并且具有严格的名称 BuildDateMacro。因此,我们将这个定义添加到主目标中:

@freestanding(expression)
macro buildDate() -> String =
  #externalMacro(module: "MyMacrosPlugin", type: "BuildDateMacro")

实际使用宏

像下面这样:

print(#buildDate)

阅读这段代码时,最重要的是要明白主要的宏功能 —— BuildDateMacro 结构体内的所有代码——在构建时运行,并将其结果注入到调用点。因此,上面的小小 print() 调用将被重写为类似于以下代码:

print("2023-06-05T18:00:00Z")

这也意味着宏内部的代码可以非常复杂,可以以任何想要的方式构建日期,因为实际上完成的代码只看到了返回的字符串。

下面我们尝试一个稍微有用一些的宏,这次是创建一个成员属性宏。当应用于类等类型时,允许将属性应用到类中的每个成员。这在概念上与旧的 @objcMembers 属性相同,将 @objc 添加到类型中的每个属性。

例如,如果有一个可观察对象,它在每个属性上使用 @Published,可以编写一个简单的 @AllPublished 宏来完成这个工作。首先,编写宏本身:

public struct AllPublishedMacro: MemberAttributeMacro {
    public static func expansion(
        of node: AttributeSyntax,
        attachedTo declaration: some DeclGroupSyntax,
        providingAttributesFor member: some DeclSyntaxProtocol,
        in context: some MacroExpansionContext
    ) throws -> [AttributeSyntax] {
        [AttributeSyntax(attributeName:SimpleTypeIdentifierSyntax(name: .identifier("Published")))]
    }
}

其次,在提供的宏列表中包含下面内容:

struct MyMacrosPlugin: CompilerPlugin {
    let providingMacros: [Macro.Type] = [
        BuildDateMacro.self,
        AllPublishedMacro.self,
    ]
}

第三,在主应用目标中声明该宏,将其标记为附加的成员属性宏:

@attached(memberAttribute)
macro AllPublished() = #externalMacro(module: "MyMacrosPlugin", type: "AllPublishedMacro")

现在可以将其用于注释可观察对象类:

@AllPublished class User: ObservableObject {
    var username = "Taylor"
    var age = 26
}

宏可以接受参数来控制其行为,尽管在这里复杂性可能会大幅上升。例如,Swift 团队的 Doug Gregor 维护着一个小的 GitHub 存储库,其中包含一些示例宏,包括一个很强大的宏,在构建时检查硬编码的 URL 是否有效 —— 这样就不可能输入错误的 URL,因为构建过程将停止。

在应用程序目标中声明宏很简单,包括添加一个字符串参数:

@freestanding(expression) public macro URL(_ stringLiteral: String) -> URL = #externalMacro(module: "MyMacrosPlugin", type: "URLMacro")

使用宏也很简单:

let url = #URL("https://swift.org")
print(url.absoluteString)

这将使 url 成为一个完整的 URL 实例,而不是一个可选值,因为在编译时检查了 URL 的正确性。

更难的是宏本身,需要读取传入的字符串 “https://swift.org” 并将其转换为 URL。Doug 的版本更加详细,但如果简化为最基本的形式,会是这样:

public struct URLMacro: ExpressionMacro {
    public static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) -> ExprSyntax {
        guard let argument = node.argumentList.first?.expression,
              let segments = argument.as(StringLiteralExprSyntax.self)?.segments
        else {
            fatalError("#URL requires a static string literal")
        }

        guard let _ = URL(string: segments.description) else {
            fatalError("Malformed url: \(argument)")
        }

        return "URL(string: \(argument))!"
    }
}

总结

首先,我们获得的 MacroExpansionContext 值具有一个非常有用的 makeUniqueName() 方法,它将生成一个新的变量名,确保不与当前上下文中的任何其他名称冲突。如果想要将新名称注入到最终的代码中,使用 makeUniqueName() 是一个明智的选择。

其次,宏的一个关注点是在遇到问题时如何调试代码——当无法轻松地逐步执行代码时,很难追踪发生了什么。在 SourceKit 中已经进行了一些工作,将宏扩展为重构操作,但是真正需要看到的是 Xcode 中的实际情况。

最后,宏所能实现的广泛转换可能意味着 Swift Evolution 本身在未来一两年内将发生变化,因为许多以前可能需要大量编译器支持和讨论的功能现在可以使用宏进行原型设计,甚至可能进行发布。文章来源地址https://www.toymoban.com/news/detail-602177.html

到了这里,关于Swift 5.9 Macros 有哪些新更新的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Swift Combine 合并多个管道以更新 UI 元素 从入门到精通十七

    Combine 系列 Swift Combine 从入门到精通一 Swift Combine 发布者订阅者操作者 从入门到精通二 Swift Combine 管道 从入门到精通三 Swift Combine 发布者publisher的生命周期 从入门到精通四 Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五 Swift Combine 订阅者Subscriber的生命周

    2024年03月13日
    浏览(35)
  • Swift Combine 通过用户输入更新声明式 UI 从入门到精通十五

    Combine 系列 Swift Combine 从入门到精通一 Swift Combine 发布者订阅者操作者 从入门到精通二 Swift Combine 管道 从入门到精通三 Swift Combine 发布者publisher的生命周期 从入门到精通四 Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五 Swift Combine 订阅者Subscriber的生命周

    2024年02月20日
    浏览(26)
  • Swift Combine 级联多个 UI 更新,包括网络请求 从入门到精通十六

    Combine 系列 Swift Combine 从入门到精通一 Swift Combine 发布者订阅者操作者 从入门到精通二 Swift Combine 管道 从入门到精通三 Swift Combine 发布者publisher的生命周期 从入门到精通四 Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五 Swift Combine 订阅者Subscriber的生命周

    2024年02月19日
    浏览(26)
  • iOS开发Swift-函数

     (1)无参函数 (2)多参函数 (3)无返回值 (4)多重返回值 (5)可选元组返回类型(元组可以是nil) (6)隐式返回的函数 任一可以被写成一行return的函数,return(x) + for。 调用的时候: 方法名(for: 参数) (1)指定参数标签 (2)忽略参数标签 (3)默认参数值 (4)可变参数 一个可变参数可接受0个或多

    2024年02月11日
    浏览(39)
  • iOS开发Swift-枚举

    枚举:一组相关的值定义了一个共同的类型,使你可以在代码中以类型安全的方式来使用这些值。 原始值:定义枚举时被预先填充的值。 (1)整数为原始值时,隐式赋值递增1。未设置原始值时,默认为0,之后递增1. (2)字符串为原始值,隐式赋值为枚举成员的名称。

    2024年02月11日
    浏览(36)
  • iOS开发Swift-闭包

    将很长的闭包表达式作为最后一个参数传递给函数,不用写出他的参数标签。 嵌套函数可捕获其外部函数所有参数、变量、常量。 当一个闭包作为一个参数传到一个函数中,但闭包在函数返回之后才被执行,则称闭包逃逸。 标注@escaping,表示允许闭包逃逸。  包装传递给函数

    2024年02月11日
    浏览(39)
  • iOS开发Swift-集合类型

    集合基本类型:数组 Array (有序), 集合 Set (无序不重复), 字典 Dictionary (无序键值对) (1)数组的表示 (2)创建空数组 (3)带值数组 (4)两数组相加创建数组 (5)字面量创造数组 (6)访问数组 (7)添加 (8)修改 (9)删除 (10)遍历 同时需要索引和值时: (1)集合的表示 (2)构造一个集合 (3)字面

    2024年02月11日
    浏览(38)
  • iOS开发Swift-基础部分

    系统可通过赋初始值进行自动推断。 平时可加可不加,同一行中有两句话必须加。 Int           UInt(最好不用) Double 64位 很大/高精度情况下使用 15位小数 Float 32位 对精度要求不高的情况下用 6位小数 十进制数   17 二进制 0b前缀 0b10001 八进制 0o前缀 0o21 十六进制 0x前缀

    2024年02月11日
    浏览(39)
  • iOS开发Swift-控制流

    (1)复合匹配 (2)区间匹配 (3)元组匹配 (4)值绑定匹配 (5)where continue, break, fallthrough, return, throw continue: 停止本次循环,开始下次循环 break: 立即结束整个控制流。可以使用break忽略switch的分支。 fallthrough贯穿: switch中的case加入贯穿,case会穿透到下一个case/ default。

    2024年02月11日
    浏览(40)
  • iOS开发Swift-类型转换

    1.Int或Double转字符串 2.Double转Int(去掉小数点后面的) 3.Int转Double 4.向上转型 5.向下转型

    2024年02月09日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包