Swift 宏(Macro)入门趣谈(一)

概述
苹果在去年 WWDC 23 中就为 Swift 语言新增了“其利断金”的重要小伙伴 Swift 宏(Swift Macro)。为此,苹果特地用 2 段视频(入门和进阶)颇为隆重的介绍了它。

那么到底 Swift 宏是什么?有什么用?它和 C/C++ 语言中的宏又有什么异同呢?本系列博文将会尝试为小伙伴们揭开 Swift 宏的神秘面纱。
在本篇博文中,您将学到如下内容:
- 概述
- 1. 从一个”尴尬“的小例子聊起
- 2. 什么是 Swift 宏?它与其它语言中的宏有何不同?
- 总结
相信学完本系列博文后,Swift Macro 会从大家心中的“阳春白雪”变为“阳阿薤露”,小伙伴们必可以将它们运用的“如臂使指”。
那还等什么呢?Let‘s go!!!😉
1. 从一个”尴尬“的小例子聊起
其实早在去年苹果祭出 Swift 5.9 时,Swift 语言本身就已经加入了超多先进和现代化的特性。
自从今年 Swift 大版本进化到 6 之后,几乎所有秃头码农们都不约而同的认为 Swift 不仅已经前所未有地成熟和稳定,而且它的能力也变得史无前例的强大。真可谓:“多平台,台台出彩;多系统;统统有戏”,貌似 Swift 已经变得“登峰造极”、“气吞山河”了。
果真如此吗?
更多关于 Swift 5.9 和 Swift 6.0 语言的全方位介绍,请小伙伴们移步我的《Swift 语言开发精讲》专栏和苹果 WWDC 24 官方站点观赏精彩的文章和视频:

- 《Swift 语言开发精讲》

- WWDC 2024
咱先别把话说满,虽说 Swift 现今已足够强大,但它真的是无所不能,丝毫不存在所谓的“阿格硫斯之踵”吗?
答案是否定的!下面我们就举一个小“栗子”让看似强大的 Swift “一秒破功”。
假设我们要实现一个很简单的功能:按类型实例的属性排序。
struct Item {let name: Stringlet nickname: String?let age: Intlet power: Double
}
在上面的 Item 中存在若干属性,我们希望写一个通用的排序方法对 Item 集合排序:
struct Model {let items: [Item]func sortItemsBy<Value: Comparable>(keyPath: KeyPath<Item, Value>, sortOrder: SortOrder = .forward) throws -> [Item] {items.sorted(using: SortDescriptor(keyPath, order: sortOrder))}
}
如上代码所示,我们在模型 Model 中创建了一个 sortItemsBy() 方法按 Item 中任意属性来排序 [Item] 数组。
比如,我们可以分别依据 Item 的 name 和 age 属性来生成不同排序后的数组:
let model = Model(items: [])
let itemsByName = try! model.sortItemsBy(keyPath: \.name)
let itemsByAge = try! model.sortItemsBy(keyPath: \.age, sortOrder: .reverse)
这样很好很强大,不过目前排序功能有点小问题:就是我们无法排序类型为可选值的属性:
let itmesByNickname = try! model.sortItemsBy(keyPath: \.nickname)
如您所见,当我们试图按 nickname 属性排序 Item 数组时,编译器就会大声抱怨。原因前面已经说了,因为 nickname 属性的类型是 String?(即 Optional<String>):

对此,我们可以提出相应的解决方法,即另外写一个可选(Optional)参数版本的排序方法:
func sortItemsBy<Value: Comparable>(keyPath: KeyPath<Item, Value?>, sortOrder: SortOrder = .forward) throws -> [Item] {items.sorted(using: SortDescriptor(keyPath, order: sortOrder))
}
这个方法与之前的几乎一毛一样,唯一不同之处在于 KeyPath 的 Value 是可选类型。

现在我们就有点“哭笑不得”了,对于排序功能我们竟然要写两个方法,即使它们几乎如出一辙。
当然对于这样简单的排序功能,我们完全可以想办法“剥离”外部的方法而直接使用 [Item] 上的 sorted() 方法。
有的小伙伴们可能会想出下面这种“左右逢源”的排序实现:
func sortItemBy<Value: Comparable>(keyPath1: KeyPath<Item, Value>?, keyPath2: KeyPath<Item, Value?>?, sortOrder: SortOrder = .forward) throws -> [Item] {if let keyPath1 {items.sorted(using: SortDescriptor(keyPath1, order: sortOrder))} else if let keyPath2 {items.sorted(using: SortDescriptor(keyPath2, order: sortOrder))} else {throw MyError("至少要有一个 KeyPath 不为 nil!")}
}
但是这样一来,我们仍然在方法内部造成了代码重复并且整体实现根本毫无优雅性可言。
与此类似的场景在偏静态的 Swift 语言中,绝对会让我们心余力绌,坐如针毡。这就是 Swift 缺乏的能力:用代码生成代码!
一般来说,我们有两种办法来解决此事:
- 在运行时动态生成代码,ruby 和 Python 就非常精于此道(所以 ruby 和 Python 之类的语言根本不需要所谓的宏);
- 由编译器(预处理器)当家做主在编译前生成代码,C/C++ 宏的强项;
将来有没有暂且不说,目前 Swift 还没有 ruby 和 Python 那种运行时动态“造码”的能力。不过从 WWDC 23 那年开始,苹果推出了 Swift 宏专注于在编译时静态“造码”。
我们的最终目标是实现下面这个 @nilable 宏,它可以生成对应版本的 KeyPath<Root, Value?> 排序方法:

看我们如何在这一共 5 篇系列博文中步步为营,最后铁杵成针的吧!
2. 什么是 Swift 宏?它与其它语言中的宏有何不同?
在官方的开发文档中,苹果就为 Macro 的特性定了基调:在编译时生成代码。 具体来说,Swift Macro 在编译时可以根据现有代码转换或生成新代码,这样做的最大好处就是避免重复(DRY)

Swift 编译器会在我们的逻辑代码编译前将其中的任何宏展开(Expands)。Swift 宏有两个重要的特性:
- 它绝不会删除已存在的代码;
- 它绝不会修改(其它)已存在的代码;
其实,在 WWDC 23 之前苹果就已经在代码中使用过海量的特殊宏,从最常见的 #function、#file、#error 宏,到 @main 、@available、@discardableResult 宏等等。我感觉在 WWDC 23 之前的某个时刻,苹果一定就有把这些特殊宏升级为通用宏的壮志凌云。
关于 Swift 宏的详细文档,大家可以到苹果官方开发站点一窥究竟:
- Swift Macros
虽然都是“宏字辈”,但是 Swift 中的宏还是与 C/C++ 等语言中的宏有些区别的:
- Swift 中的宏完全参与到 Swift 的类型系统中,可以在编译时保证类型安全;
- 苹果确保宏的展开过程是一个沙箱(Sandbox)操作,不会带来安全漏洞或泄露系统中用户的隐私;
- 在宏展开发生错误时,Swift Macros 提供了更多的机制来帮助用户快速了解错误原因和解决办法;
- 我们可以为宏编写单元测试,增强它的鲁棒性;
因为 C/C++ 中的宏只是一个简单的字符串字面值替换器,所以无论是用户编写和编译器支持起来都不算太难,但是 Swift 的宏就完全是另一回事了。
想要系统学习 Swift 的小伙伴们,欢迎来我的 《Swift语言开发精讲》专栏逛一逛哦。

- 《Swift 语言开发精讲》
在了解了 Swift 宏之后,在下一篇博文中我们就来看看它能做些什么以及不要用它们做什么?
总结
在本篇博文中,我们讨论了 Swift 宏的基本概念,以及它与 C/C++ 语言中的宏有何不同。
感谢观赏,我们下一篇见!😎
相关文章:
Swift 宏(Macro)入门趣谈(一)
概述 苹果在去年 WWDC 23 中就为 Swift 语言新增了“其利断金”的重要小伙伴 Swift 宏(Swift Macro)。为此,苹果特地用 2 段视频(入门和进阶)颇为隆重的介绍了它。 那么到底 Swift 宏是什么?有什么用&…...
linux常见资源查询命令(持续更新)
年纪大了,很多命令记不住了,但偶尔也需要用到,通过搜索也需要点时间,特此记录。 不同操作系统命令会有所区别,下面是大部分时候工作的机器系统: CentOS release 7.5 (Final)Kernel \r on an \m 1、实时查…...
JavaWeb:文件上传1
欢迎来到“雪碧聊技术”CSDN博客! 在这里,您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者,还是具有一定经验的开发者,相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导,我将…...
C++ 中的异常处理机制是怎样的?
异常处理的基本概念: 异常: 程序在运行时发生的错误或意外情况。 抛出异常: 使用 throw 关键字将异常传递给调用堆栈。 捕获异常: 使用 try-catch 块捕获和处理异常。 异常类型: 表示异常类别的标识符。 异常处理流程: 抛出异常: 当检测到错误或意…...
SwiftUI-基础入门
开发OS X 图形应用界面时有三种实现方式:XIB、Storyboard、SwiftUI。Storyboard基于XIB做了优化,但XIB基本被放弃了,而SwiftUI是苹果公司后来开发的一套编程语言,用来平替Objective-C。虽然现在Swift 6 还是有些不完善的地方&…...
C++builder中的人工智能(20):如何在C++中开发一个简单的Hopfield网络
在AI技术的发展历史中,模式识别模型是最伟大的AI技术之一,尤其是从像素图像中读取文本。其中一个是Hopfield网络(或称为Ising模型的神经网络或Ising–Lenz–Little模型),这是一种递归神经网络形式,由John J…...
video2gif容器构建指南
一、介绍 1.项目概述 Video2Gif 项目旨在提供一种便捷的方式,让用户能够将视频中的精彩片段快速转换为 GIF 动画。GIF 动画因其循环播放、文件体积小等特点,在社交媒体、聊天工具中广泛应用,用于表达情感、分享趣事等。 2.核心功能 视频导…...
探秘Spring Boot中的@Conditional注解
文章目录 1. 什么是Conditional注解?2. 为什么需要Conditional注解?3. 如何使用Conditional注解?4. Conditional注解的高级用法5. 注意事项6. 结语推荐阅读文章 在Spring Boot的世界里,配置的灵活性和多样性是至关重要的。有时候&…...
树形dp总结
这类题型在 dp 中很常见,于是做一个总结吧!!! 最经典的题:没有上司的舞会 传送门:没有上司的舞会 - 洛谷 状态表示: dp[i][0] 为 以 i 为根的子树中,选择 i 节点的最大欢乐值 d…...
【算法一周目】双指针(2)
目录 有效三角形的个数 解题思路 C代码实现 和为s的两个数字 解题思路 C代码实现 三数之和 解题思路 C代码实现 四数之和 解题思路 C代码实现 有效三角形的个数 题目链接:611. 有效三角形的个数题目描述:给定一个包含非负整数的数组nums&…...
vue内置方法总结
目录 1. 生命周期钩子方法 2. 响应式系统方法 3. DOM 更新方法 4. 事件处理方法 5. 访问子组件和 DOM 元素 6. 数据观察方法 7. 其他方法 1. 生命周期钩子方法 这些方法在 Vue 实例的不同生命周期阶段自动调用。 beforeCreate: 在实例初始化之后,…...
面向对象分析与设计
前言: 感觉书本上和线上课程, 讲的太抽象, 不好理解, 但软件开发不就是为了开发应用程序吗?! 干嘛搞这么抽象,对吧, 下面是个人对于软件开发的看法, 结合我的一些看法, 主打简单易懂, 当然,我一IT界小菜鸟, 对软件开发的认识也很浅显, 这个思维导图也仅仅是现阶段我的看…...
lineageos-19 仓库群遍历,打印第一条git log
lineageos-19 仓库群遍历,打印第一条git log RepoLsRootD/app4/lineage19_oneplus6 LogF/app4/wiki/repo_head_log_ls-lineageos19.1.log rm -v $LogF && \ cd $RepoLsRootD && \ find . -type l -path "*/*.git" -not -path "./.repo/*"…...
详解基于C#开发Windows API的SendMessage方法的鼠标键盘消息发送
在C#中,SendMessage方法是一个强大的工具,它允许我们与Windows API交互,模拟键盘和鼠标事件。本文将详细介绍如何使用SendMessage方法来发送鼠标和键盘消息。 1. SendMessage方法概述 SendMessage是Windows API中的一个函数,它用…...
VMware安装黑苹果后ICLOUD_UNSUPPORTED_DEVICE(不支持的Icloud设备)
修改文件 关闭虚拟机找到虚拟机文件中以.vmx结尾的文件编辑内容(补充缺失) board-id "Mac-551B86E5744E2388" hw.model.reflectHost "FALSE" hw.model "MacBookPro14,3" serialNumber.reflectHost "FALSE"…...
Python | Leetcode Python题解之第542题01矩阵
题目: 题解: class Solution:def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]:m, n len(matrix), len(matrix[0])# 初始化动态规划的数组,所有的距离值都设置为一个很大的数dist [[10**9] * n for _ in range(m)]…...
【计算机网络】【传输层】【习题】
计算机网络-传输层-习题 文章目录 10. 图 5-29 给出了 TCP 连接建立的三次握手与连接释放的四次握手过程。根据 TCP 协议的工作原理,请填写图 5-29 中 ①~⑧ 位置的序号值。答案技巧 注:本文基于《计算机网络》(第5版)吴功宜、吴英…...
【LeetCode】【算法】55. 跳跃游戏
LeetCode 99 - 55. 跳跃游戏 题目 给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标,如果可以,返回true;否则,返回 …...
华为:hcia综合实验
一、拓扑图 二、实验要求 1. pc地址请自行规划,vlan已给出 2. 服务器地址自行规划,vlan,网段已给出 3. 交换机互联链路捆绑保证冗余性 4. 内网pc网关集中于核心交换机,交换机vlan 40互联路由器 ,地址网段已给出 5.配置静态路由实…...
MyBatis与MyBatis-Plus(基础)
MyBatis-Plus的优势 在 Spring Data JPA 已经很方便的情况下,有时仍然选择使用 MyBatis-Plus 的核心原因主要有以下三点: 1. 复杂 SQL 控制能力更强 MyBatis-Plus 允许直接编写和优化 SQL,适合复杂查询、精细化 SQL 控制的场景。特别是在性…...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
