You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift动态值Accessor Macro:如何实现关联存储属性的GetSetMacro

Swift Accessor宏GetSetMacro实现问题

我想编写一个名为GetSetMacro的Swift Accessor宏,它接收类型中存储属性的路径(例如\.value),为另一个属性生成基于该存储属性的访问器。已完成宏的定义与部分实现,但在expansion方法中无法确定如何访问类型的成员值。相关代码、示例用法及预期展开效果如下:

宏定义

@attached(accessor)
public macro GetSetMacro<Value>(_ : Value)  = #externalMacro(module: "MyMacroModule", type: "GetSetMacro")

待完善的宏实现

public struct GetSetMacro: AccessorMacro {
    public static func expansion(of node: SwiftSyntax.AttributeSyntax, providingAccessorsOf declaration: some SwiftSyntax.DeclSyntaxProtocol, in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.AccessorDeclSyntax] {
        guard
             case let .argumentList(arguments) = node.arguments,
             let argument = arguments.first
           else { return [] }
       
        return [
            """
              get {
                // 如何在此处访问类型的成员值?
                ???
              }
              """,
              """
              set {
                ??? = newValue
              }
            """
        ]
    }
}

示例用法

var value = 1

@GetSetMacro(\.value)
var newIntValue: Int

预期展开结果

var newIntValue {
 get {
   value
 } set {
    value = newValue
 }
}

解决方案

核心是从传入的属性路径参数中提取成员名称,再将其插入到访问器代码模板中。

修改后的完整宏实现

import SwiftSyntax
import SwiftSyntaxMacros

public struct GetSetMacro: AccessorMacro {
    public static func expansion(of node: SwiftSyntax.AttributeSyntax, providingAccessorsOf declaration: some SwiftSyntax.DeclSyntaxProtocol, in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.AccessorDeclSyntax] {
        guard
            case let .argumentList(arguments) = node.arguments,
            let argument = arguments.first?.expression,
            // 解析属性路径表达式,提取成员标识符
            let keyPathExpr = argument.as(KeyPathExprSyntax.self),
            let component = keyPathExpr.components.first?.as(KeyPathComponentExprSyntax.self),
            let identifier = component.expression.as(IdentifierExprSyntax.self)
        else {
            throw MacroExpansionError(message: "请传入有效的属性路径,格式如\\.value")
        }
        
        let memberName = identifier.identifier.text
        
        // 生成get访问器
        let getAccessor = try AccessorDeclSyntax("""
            get {
                \(raw: memberName)
            }
            """)
        
        // 生成set访问器
        let setAccessor = try AccessorDeclSyntax("""
            set {
                \(raw: memberName) = newValue
            }
            """)
        
        return [getAccessor, setAccessor]
    }
}

关键细节

  1. 属性路径解析:通过KeyPathExprSyntax拆解传入的\.value表达式,提取出对应的成员名称value。
  2. 动态生成代码:将提取到的成员名称直接嵌入到get和set的代码模板中,生成符合预期的访问器语法节点。
  3. 参数校验:增加了错误判断,若传入的参数不是有效的属性路径,会抛出明确的错误提示。

修改后,示例中的宏注解将正确展开为预期的计算属性访问器,直接访问目标成员变量。

内容的提问来源于stack exchange,提问作者hariszaman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 18:53:13