广州专业建网站公司,宝应百度seo,网红推广团队去哪里找,wordpress 作者昵称概述
苹果在去年 WWDC 23 中就为 Swift 语言新增了“其利断金”的重要小伙伴 Swift 宏#xff08;Swift Macro#xff09;。为此#xff0c;苹果特地用 2 段视频#xff08;入门和进阶#xff09;颇为隆重的介绍了它。 那么到底 Swift 宏是什么#xff1f;有什么用…
概述
苹果在去年 WWDC 23 中就为 Swift 语言新增了“其利断金”的重要小伙伴 Swift 宏Swift Macro。为此苹果特地用 2 段视频入门和进阶颇为隆重的介绍了它。 那么到底 Swift 宏是什么有什么用它和 C/C 语言中的宏又有什么异同呢本系列博文将会尝试为小伙伴们揭开 Swift 宏的神秘面纱。 在本篇博文中您将学到如下内容 概述5.4 完成宏主体5.5 验证宏展开后的结果 6. 另一个简洁的解决方案7. 目前 Swift 宏的不足之处总结 相信学完本系列博文后Swift Macro 会从大家心中的“阳春白雪”变为“阳阿薤露”小伙伴们必可以将它们运用的“如臂使指”。
那还等什么呢Let‘s go 5.4 完成宏主体
在完成了 nilable 宏接口、客户端代码以及宏主体初步结构的搭建之后现在回到 NilableMacro 结构的定义中我们现在可以尝试补全 expansion() 方法中所有展开代码了。
不过在撸码之前我们有必要先来了解一下 expansion() 方法传入实参所扮演的角色。在这个例子中我们只关心其中的 declaration 参数它是一个 some DeclSyntaxProtocol 类型
providingPeersOf declaration: some DeclSyntaxProtocol在 Xcode 调试控制台中我们利用 po 命令可以列出它的结构细节
FunctionDeclSyntax
├─attributes: AttributeListSyntax
│ ╰─[0]: AttributeSyntax
│ ├─atSign: atSign
│ ╰─attributeName: IdentifierTypeSyntax
│ ╰─name: identifier(nilable)
├─modifiers: DeclModifierListSyntax
│ ├─[0]: DeclModifierSyntax
│ │ ╰─name: keyword(SwiftSyntax.Keyword.static)
│ ╰─[1]: DeclModifierSyntax
│ ╰─name: keyword(SwiftSyntax.Keyword.private)
├─funcKeyword: keyword(SwiftSyntax.Keyword.func)
├─name: identifier(test)
├─genericParameterClause: GenericParameterClauseSyntax
│ ├─leftAngle: leftAngle
│ ├─parameters: GenericParameterListSyntax
│ │ ├─[0]: GenericParameterSyntax
│ │ │ ├─attributes: AttributeListSyntax
│ │ │ ├─name: identifier(Root)
│ │ │ ╰─trailingComma: comma
│ │ ╰─[1]: GenericParameterSyntax
│ │ ├─attributes: AttributeListSyntax
│ │ ├─name: identifier(Value)
│ │ ├─colon: colon
│ │ ╰─inheritedType: IdentifierTypeSyntax
│ │ ╰─name: identifier(Comparable)
│ ╰─rightAngle: rightAngle
├─signature: FunctionSignatureSyntax
│ ├─parameterClause: FunctionParameterClauseSyntax
│ │ ├─leftParen: leftParen
│ │ ├─parameters: FunctionParameterListSyntax
│ │ │ ├─[0]: FunctionParameterSyntax
│ │ │ │ ├─attributes: AttributeListSyntax
│ │ │ │ ├─modifiers: DeclModifierListSyntax
│ │ │ │ ├─firstName: identifier(dump)
│ │ │ │ ├─secondName: identifier(a)
│ │ │ │ ├─colon: colon
│ │ │ │ ├─type: IdentifierTypeSyntax
│ │ │ │ │ ├─name: identifier(KeyPath)
│ │ │ │ │ ╰─genericArgumentClause: GenericArgumentClauseSyntax
│ │ │ │ │ ├─leftAngle: leftAngle
│ │ │ │ │ ├─arguments: GenericArgumentListSyntax
│ │ │ │ │ │ ├─[0]: GenericArgumentSyntax
│ │ │ │ │ │ │ ├─argument: IdentifierTypeSyntax
│ │ │ │ │ │ │ │ ╰─name: identifier(Root)
│ │ │ │ │ │ │ ╰─trailingComma: comma
│ │ │ │ │ │ ╰─[1]: GenericArgumentSyntax
│ │ │ │ │ │ ╰─argument: IdentifierTypeSyntax
│ │ │ │ │ │ ╰─name: identifier(Value)
│ │ │ │ │ ╰─rightAngle: rightAngle
│ │ │ │ ╰─trailingComma: comma
│ │ │ ├─[1]: FunctionParameterSyntax
│ │ │ │ ├─attributes: AttributeListSyntax
│ │ │ │ ├─modifiers: DeclModifierListSyntax
│ │ │ │ ├─firstName: identifier(b)
│ │ │ │ ├─colon: colon
│ │ │ │ ├─type: IdentifierTypeSyntax
│ │ │ │ │ ╰─name: identifier(String)
│ │ │ │ ╰─trailingComma: comma
│ │ │ ╰─[2]: FunctionParameterSyntax
│ │ │ ├─attributes: AttributeListSyntax
│ │ │ ├─modifiers: DeclModifierListSyntax
│ │ │ ├─firstName: identifier(c)
│ │ │ ├─colon: colon
│ │ │ ╰─type: IdentifierTypeSyntax
│ │ │ ╰─name: identifier(Int)
│ │ ╰─rightParen: rightParen
│ ├─effectSpecifiers: FunctionEffectSpecifiersSyntax
│ │ ╰─throwsClause: ThrowsClauseSyntax
│ │ ╰─throwsSpecifier: keyword(SwiftSyntax.Keyword.throws)
│ ╰─returnClause: ReturnClauseSyntax
│ ├─arrow: arrow
│ ╰─type: IdentifierTypeSyntax
│ ╰─name: identifier(Bool)
╰─body: CodeBlockSyntax├─leftBrace: leftBrace├─statements: CodeBlockItemListSyntax│ ╰─[0]: CodeBlockItemSyntax│ ╰─item: PrefixOperatorExprSyntax│ ├─operator: prefixOperator(!)│ ╰─expression: MemberAccessExprSyntax│ ├─base: DeclReferenceExprSyntax│ │ ╰─baseName: identifier(b)│ ├─period: period│ ╰─declName: DeclReferenceExprSyntax│ ╰─baseName: identifier(isEmpty)╰─rightBrace: rightBrace代码看起来很长似乎有点儿“一望无际”。不过如果我们查看它的 description 属性就会恍然大悟它其实就是 nilable 宏修饰方法所对应的语法树。 所以基本上来说我们只需要将 declaration 的内容“修剪”为想要的结果即可。对于我们这个例子就是将 declaration 中第一个 KeyPath 参数中的 Value 变为可选类型 Value?。
下面是我们第一种实现因为考虑到了“调皮的”用户可能犯的各种错误所以比较冗长。不过别急后面我们会给出精简后的方案
public static func expansion(of node: AttributeSyntax, providingPeersOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext) throws - [DeclSyntax] {guard let funcDecl declaration.as(FunctionDeclSyntax.self) else {throw MacroExpansionErrorMessage(必须在方法上使用我哦)}let funcName funcDecl.namelet funcModifiers funcDecl.modifierslet funcGenericParameterClause funcDecl.genericParameterClauselet funcEffecSpecifier funcDecl.signature.effectSpecifiers?.description ?? let funcReturnClause funcDecl.signature.returnClause?.description ?? let funcBody funcDecl.bodyguard let funcFirstParamenter funcDecl.signature.parameterClause.parameters.first else {throw MacroExpansionErrorMessage(方法必须至少要有 1 个参数哦)}guard let funcFirstParamenterType funcFirstParamenter.type.as(IdentifierTypeSyntax.self) else {throw MacroExpansionErrorMessage(方法参数格式错误哦)}guard funcFirstParamenterType.name.description KeyPath else {throw MacroExpansionErrorMessage(方法的第一个参数必须是 KeyPath 类型哦)}guard let secondArgment funcFirstParamenterType.genericArgumentClause?.arguments.last, secondArgment.argument.as(OptionalTypeSyntax.self) nil else {throw MacroExpansionErrorMessage(方法 KeyPath 参数中的 Value 不是 Optional 类型哦)}let firstName funcFirstParamenter.firstName.descriptionvar firstArg if let secondName funcFirstParamenter.secondName?.description {\(firstName)\(secondName): } else {\(firstName): }let rootName funcFirstParamenterType.genericArgumentClause!.arguments.first!.argument.as(IdentifierTypeSyntax.self)!.name.descriptionlet valueName secondArgment.argument.as(IdentifierTypeSyntax.self)!.descriptionfirstArg KeyPath\(rootName), \(valueName)?let parameters funcDecl.signature.parameterClause.parametersvar otherParametersDesc if parameters.count 1 {for parameter in parameters.dropFirst() {otherParametersDesc \(parameter.description)}}let nilFuncDecl try FunctionDeclSyntax(\(funcModifiers)func \(funcName)\(funcGenericParameterClause)(\(raw: firstArg), \(raw: otherParametersDesc)) \(raw: funcEffecSpecifier)\(raw: funcReturnClause) \(funcBody))return [.init(nilFuncDecl)]
}在上面的代码中我们做了这样几件事
为用户在构造宏所修饰的表达式时可能犯的各种错误比如用户在属性而不是方法上应用 nilable 宏提供贴心的提示确定 KeyPath 中 Value 的名称考虑到 KeyPath 参数可能出现的名称前缀确定方法签名中的其它信息比如其它参数类型和返回类型考虑到方法本身可能存在一些 modifier 和 effectSpecifiers 修饰器比如 static、private、throws 等
5.5 验证宏展开后的结果
回到 main.swift 源代码文件中在选中 nilable 宏关键字后将其展开如果不出意外我们应该可以正确创建 sortItemsBy 排序方法的 KeyPath 可选 Value 版本Value 这意味着下面这些代码都可以顺利编译通过了
let itemsByName try! model.sortItemsBy(keyPath: \.name)
let itmesByNickname try! model.sortItemsBy(keyPath: \.nickname)棒棒哒成就感爆棚的小伙伴们赶快给自已一个大大的赞吧 其实验证 Swift 宏展开结果最好的方法是使用单元测试但由于目前宏单元测试略显“呆滞和笨拙”所以暂时略去不表。 6. 另一个简洁的解决方案
虽然我们解决了问题但上述实现实在是有点“臃肿不堪”。
让我们马上再做一次头脑风暴我们实际只是想修改 sortItemsBy() 方法 KeyPath 形参中的 Value如果只考虑几种可能出现的错误仅需一个简单的字符串内容替换操作即可大功告成
下面是我们 expansion() 方法简化后的新实现
public static func expansion(of node: AttributeSyntax, providingPeersOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext) throws - [DeclSyntax] {guard let funcDecl declaration.as(FunctionDeclSyntax.self) else {throw MacroExpansionErrorMessage(必须在方法上使用我哦)}var funcDesc funcDecl.descriptionfuncDesc.replace(/nilable/, with: )let valueFinder /.*KeyPath.?,\s*(.),/guard let value try valueFinder.firstMatch(in: funcDesc)?.output.1 else {throw MacroExpansionErrorMessage(找不到 KeyPath )}funcDesc.replaceSubrange(value.startIndex..value.endIndex, with: \(value)?) return [.init(try FunctionDeclSyntax(\(raw: funcDesc)))]
}在上面的代码中我们只是仅仅替换原方法里形参 KeyPath 中的值类型 Value 为 Value? 而已真是简单的不要不要的。
验证 nilable 宏展开分结果和之前是一毛一样的棒棒哒
7. 目前 Swift 宏的不足之处
虽然 Swift Macros 更好地弥补了 Swift 语言动态性不足的“先天缺陷”让秃头码农们更优雅的遵循 “KISS” 原则避免了代码重复。
但是目前 Swift 宏仍有一些白圭之玷它们主要表现在如下几个方面
无论我们是否喜欢即使一个非常简单的宏实现都需要放在 Swift Package 中官方示例很少研究起来比较容易掉头发定义 7 种宏的“隐藏”选项几乎没有文档更容易掉头发FunctionDeclSyntax 等一些声明语法实体都是只读的不太灵活虽然像 FunctionDeclSyntax 这些声明语法实体都包含 ResultBuilder 构造器但实际无法像 SwiftUI 那样自由组合内部元素因为要实现庞大 Swift 语言的类型安全所以 Swift 宏背后语法树的结构也很复杂经常云里雾里的感觉貌似宏的实现有缓存所以在 Xcode 16 中修改宏的展开代码有时不能及时触发测试用例中宏展开结果的刷新需要重启 Xcode
以上只是其中一部分“美中不足”其它一些小问题并没有完全列出来。
其实现在 Swift 宏最大的问题就是它缺乏文档而且太复杂了这就是为什么目前没什么人用的原因。
希望苹果在 WWDC 25 的 Swift Macros 2.0 中如果可能的话可以简化和改善它。
即便如此雪中送炭的 Swift 宏仍然给了我们太多惊喜和便利值得小伙伴们进一步深入挖掘。
本系列文章至此告一段落了有机会我们会单写几篇 Swift 宏的进阶博文敬请期待吧
总结
在本篇博文中我们介绍了宏展开方法中 declaration 参数的构成FunctionDeclSyntax 并详细讨论了自定义宏主体的实现我们随后还精简了宏的展开逻辑并顺便聊了聊当前 Swift 宏的一些不足之处。
感谢观赏再会啦 文章转载自: http://www.morning.lgznc.cn.gov.cn.lgznc.cn http://www.morning.hwpcm.cn.gov.cn.hwpcm.cn http://www.morning.clkjn.cn.gov.cn.clkjn.cn http://www.morning.jjnry.cn.gov.cn.jjnry.cn http://www.morning.wwthz.cn.gov.cn.wwthz.cn http://www.morning.cfhwn.cn.gov.cn.cfhwn.cn http://www.morning.kjfqf.cn.gov.cn.kjfqf.cn http://www.morning.yrnll.cn.gov.cn.yrnll.cn http://www.morning.dxpqd.cn.gov.cn.dxpqd.cn http://www.morning.plfrk.cn.gov.cn.plfrk.cn http://www.morning.qkxt.cn.gov.cn.qkxt.cn http://www.morning.mtyhk.cn.gov.cn.mtyhk.cn http://www.morning.pqwrg.cn.gov.cn.pqwrg.cn http://www.morning.nynpf.cn.gov.cn.nynpf.cn http://www.morning.qjngk.cn.gov.cn.qjngk.cn http://www.morning.sxmbk.cn.gov.cn.sxmbk.cn http://www.morning.wpspf.cn.gov.cn.wpspf.cn http://www.morning.xrlwr.cn.gov.cn.xrlwr.cn http://www.morning.gmnmh.cn.gov.cn.gmnmh.cn http://www.morning.hyxwh.cn.gov.cn.hyxwh.cn http://www.morning.cspwj.cn.gov.cn.cspwj.cn http://www.morning.ptdzm.cn.gov.cn.ptdzm.cn http://www.morning.ghxtk.cn.gov.cn.ghxtk.cn http://www.morning.pdbgm.cn.gov.cn.pdbgm.cn http://www.morning.xgmf.cn.gov.cn.xgmf.cn http://www.morning.rglp.cn.gov.cn.rglp.cn http://www.morning.jkpnm.cn.gov.cn.jkpnm.cn http://www.morning.qmkyp.cn.gov.cn.qmkyp.cn http://www.morning.lkkkf.cn.gov.cn.lkkkf.cn http://www.morning.bksbx.cn.gov.cn.bksbx.cn http://www.morning.trnl.cn.gov.cn.trnl.cn http://www.morning.hdqqr.cn.gov.cn.hdqqr.cn http://www.morning.dtrzw.cn.gov.cn.dtrzw.cn http://www.morning.yhyqg.cn.gov.cn.yhyqg.cn http://www.morning.yjfzk.cn.gov.cn.yjfzk.cn http://www.morning.tyklz.cn.gov.cn.tyklz.cn http://www.morning.sjftk.cn.gov.cn.sjftk.cn http://www.morning.xhklb.cn.gov.cn.xhklb.cn http://www.morning.byywt.cn.gov.cn.byywt.cn http://www.morning.zdmlt.cn.gov.cn.zdmlt.cn http://www.morning.rmltt.cn.gov.cn.rmltt.cn http://www.morning.kqgqy.cn.gov.cn.kqgqy.cn http://www.morning.rxrw.cn.gov.cn.rxrw.cn http://www.morning.mnqz.cn.gov.cn.mnqz.cn http://www.morning.mmosan.com.gov.cn.mmosan.com http://www.morning.lsfrc.cn.gov.cn.lsfrc.cn http://www.morning.qwzpd.cn.gov.cn.qwzpd.cn http://www.morning.ngdkn.cn.gov.cn.ngdkn.cn http://www.morning.jbblf.cn.gov.cn.jbblf.cn http://www.morning.xknsn.cn.gov.cn.xknsn.cn http://www.morning.dxqwm.cn.gov.cn.dxqwm.cn http://www.morning.redhoma.com.gov.cn.redhoma.com http://www.morning.tymnr.cn.gov.cn.tymnr.cn http://www.morning.hctgn.cn.gov.cn.hctgn.cn http://www.morning.ttrdr.cn.gov.cn.ttrdr.cn http://www.morning.pjtw.cn.gov.cn.pjtw.cn http://www.morning.ngqty.cn.gov.cn.ngqty.cn http://www.morning.pycpt.cn.gov.cn.pycpt.cn http://www.morning.jfmyt.cn.gov.cn.jfmyt.cn http://www.morning.lsxabc.com.gov.cn.lsxabc.com http://www.morning.mdfxn.cn.gov.cn.mdfxn.cn http://www.morning.sqtsl.cn.gov.cn.sqtsl.cn http://www.morning.kqbjy.cn.gov.cn.kqbjy.cn http://www.morning.nywrm.cn.gov.cn.nywrm.cn http://www.morning.wmrgp.cn.gov.cn.wmrgp.cn http://www.morning.tjqcfw.cn.gov.cn.tjqcfw.cn http://www.morning.ykmtz.cn.gov.cn.ykmtz.cn http://www.morning.kxscs.cn.gov.cn.kxscs.cn http://www.morning.xphls.cn.gov.cn.xphls.cn http://www.morning.cwgpl.cn.gov.cn.cwgpl.cn http://www.morning.frpb.cn.gov.cn.frpb.cn http://www.morning.jkrrg.cn.gov.cn.jkrrg.cn http://www.morning.ypqwm.cn.gov.cn.ypqwm.cn http://www.morning.fqmbt.cn.gov.cn.fqmbt.cn http://www.morning.ywzqk.cn.gov.cn.ywzqk.cn http://www.morning.hgbzc.cn.gov.cn.hgbzc.cn http://www.morning.cgntj.cn.gov.cn.cgntj.cn http://www.morning.srkwf.cn.gov.cn.srkwf.cn http://www.morning.bwqr.cn.gov.cn.bwqr.cn http://www.morning.gcspr.cn.gov.cn.gcspr.cn