Swift 5.9 Macros 有哪些新更新

news2024/11/23 4:33:04

在这里插入图片描述

文章目录

    • 前言
    • Macros(宏)
    • 需要了解的关键信息
    • 环境准备
    • 创建一个宏
      • 定义宏
      • 实际使用宏
    • 总结

前言

虽然 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

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

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 个部分,截图示意如下

它们分别是

  • 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 本身在未来一两年内将发生变化,因为许多以前可能需要大量编译器支持和讨论的功能现在可以使用宏进行原型设计,甚至可能进行发布。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/880783.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

echarts多条折线图

代码 <template><div><!-- 折线图 --><div id"average-score1" class"risk-percent" /></div> </template><script> import * as echarts from "echarts";export default {name: "StrategicRis…

深入了解Bear Necessities Hackathon黑客松的优胜者们

生态系统中的资深建设者皆知道Moonbeam是大型黑客松狂热爱好者&#xff0c;不论是线上虚拟的还是现场的。然而&#xff0c;很少有黑客松能达到Moonbeam和AWS举办的Bear Necessities黑客松这样的规模和吸引力。本次黑客松共有755人参与&#xff0c;共提交了62份参赛作品。其中22…

普通上班族学Python有用吗?

普通上班族学Python有用吗&#xff1f;对于广大上班族而言&#xff0c;时间和精力主要问题&#xff0c;学习Python编程语言为了能提高工作效率。学Python不是单纯的为了增加知识储备&#xff0c;Python本质上是一个工具和手段&#xff0c;最终目的是要通过它来帮我们解决实际工…

高并发数据抓取实战:使用HTTP爬虫ip提升抓取速度

又到每天一期学习爬虫的时间了&#xff0c;作为一名专业的爬虫程序员&#xff0c;今天要跟你们分享一个超实用的技巧&#xff0c;就是利用HTTP爬虫ip来提升高并发数据抓取的速度。听起来有点高大上&#xff1f;别担心&#xff0c;我会用通俗易懂的话来和你们说&#xff0c;让你…

【奥义】如何用ChatGPT写论文搞模型

目录 你是否曾经在复现科研论文的结果时感到困难重重&#xff1f; 引言 1 打开需要复现的目标文献 2 提取公式定义的语句 3 文章公式、图实现 &#xff08;1&#xff09;用python复现目标文献中的公式 &#xff08;2&#xff09;用python复现目标文献中的图 4 Copy代码…

数据结构——配对堆

引入 配对堆是一个支持插入&#xff0c;查询/删除最小值&#xff0c;合并&#xff0c;修改元素等操作的数据结构&#xff0c;是一种可并堆。有速度快和结构简单的优势&#xff0c;但由于其为基于势能分析的均摊复杂度&#xff0c;无法可持久化。 定义 配对堆是一棵满足堆性质…

ntfy Delphi 相关消息接口文档

关联文档&#xff1a; ntfy 实现消息订阅和通知&#xff08;无需注册、无需服务器&#xff0c;太好了&#xff09;_海纳老吴的博客-CSDN博客群晖 nas 自建 ntfy 通知服务&#xff08;梦寐以求&#xff09;_海纳老吴的博客-CSDN博客 目录 一、消息实体对象接口 1. 消息发布方…

ssm营业厅宽带系统源码和论文

ssm简易版营业厅宽带系统源码和论文018 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c…

华为OD机试 - 查找众数及中位数(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

在Windows Server 2008上启用自动文件夹备份

要在Windows Server 2008上启用自动文件夹备份&#xff0c;您可以使用内置的Windows备份功能。下面是如何设置它的方法&#xff1a; 1. 点击“开始”按钮并选择“服务器管理器”&#xff0c;打开“服务器管理器”。 2. 在“服务器管理器”窗口中&#xff0c;单击左侧窗格中的“…

Failed to connect to bitbucket.org port 443

浏览器可以访问bitbucket&#xff0c;但是在终端或者sourcetree上死活无法进行pull, push等操作。 Root Cause&#xff1a;“【翻】【墙】软件”使用了http proxy&#xff0c;所以也得为git设置相同的http proxy。 所以&#xff0c;解决方法是&#xff1a; 1&#xff0c;查看“…

【环境配置】Windows 10 安装 PyTorch 开发环境,以及验证 YOLOv8

Windows 10 安装 PyTorch 开发环境&#xff0c;以及验证 YOLOv8 最近搞了一台Windows机器&#xff0c;准备在上面安装深度学习的开发环境&#xff0c;并搭建部署YOLOv8做训练和测试使用&#xff1b; 环境&#xff1a; OS&#xff1a; Windows 10 显卡&#xff1a; RTX 3090 安…

YOLOv8 : 网络结构

一. YOLOv8网络结构 1. Backbone YOLOv8的Backbone同样参考了CSPDarkNet-53网络&#xff0c;我们可以称之为CSPDarkNet结构吧&#xff0c;与YOLOv5不同的是&#xff0c;YOLOv8使用C2f(CSPLayer_2Conv)代替了C3模块(如果你比较熟悉YOLOv5的网络结构&#xff0c;那YOLOv8的网络…

非线性 简介

让我们分析一下现实世界物体的运动规律 摇摇头&#xff0c;感受一下你的头是怎样运动的 我们的头侧向一方&#xff0c;准备往另一边转动时&#xff0c;先加速&#xff0c;等快要到达目标时&#xff0c;马上减速。 这是摇头时头的运动曲线 加速的时间很短&#xff0c;所以看起来…

【eNSP】OSPF实验

【eNSP】OSPF实验 原理术语Router-idOSPF区域DR与DDR 过程 实验根据图片连接模块配置设备名称和IP地址修改R1&#xff1a;修改R2&#xff1a;修改R3修改R4修改R5测试连通性 OSPF设置设置进程号和RID划分OSPF区域DR设置&#xff0c;2way实验设置查看设置结果 口令验证 原理 OSP…

控制威格士伺服阀放大器SM4-15、SX4-10、SX4-12

威格士的SM4-10、SM4-12、SM4-15、SX4-10、SX4-12、SX4-15、SM4-20、SX4-20、SM4-30、SM4-40系列伺服阀搭配模块式伺服放大器提供系统闭环控制&#xff0c;具有位置精度高、速度曲线可重复以及可预测的力或力矩的调节等优点。 伺服阀的典型应用包括注塑&#xff0f;吹塑成形系…

低代码开发工具:JVS轻应用之间如何实现数据的调用?

在低代码开发平台中&#xff0c;如何实现应用之间的数据共享呢&#xff1f;最标准的方式是通过接口&#xff0c;本文介绍JVS轻应用如何实现将数据通过API输出、轻应用如何实现体内API数据的获取&#xff1f;实现方式如下图所示&#xff0c;不管是数据提供方&#xff0c;还是数据…

ebay灯串UL报告 UL588检测标准

季节性和装饰性照明用品即灯串以及配件都是便携式插头连接的临时性商品&#xff0c;最大额定输入电压为 120 伏。 由 ILAC ISO 17025 认证的实验室出具的检测报告&#xff0c;确认每件商品均已经过检测&#xff0c;符合下列要求&#xff1a; 季节性和装饰性照明用品(灯串&…

idea下载安装教程

idea下载安装教程 文章目录 idea下载安装教程1、下载2、安装 1、下载 进入官网&#xff1a; https://www.jetbrains.com/ 下滑&#xff1a; 点击Download 这个就是专业版了&#xff0c;需要付费&#xff0c;学生认证就是用的这个专业版的 但是2023的版本对于页面做了很大的改…

物联网工程应用实训室建设方案

一、物联网工程应用系统概述 1.1物联网工程定义 物联网工程&#xff08;Internet of Things Engineering&#xff09;是一种以信息技术&#xff08;IT&#xff09;来改善实体世界中人们生活方式的新兴学科&#xff0c;它利用互联网技术为我们的日常生活活动提供服务和增益&am…