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

如何用Swift宏为结构体添加成员并增强其初始化方法?

问题解答

1. 是否存在修改现有初始化器的宏角色?

目前Swift宏体系中,没有专门用于修改已有声明(比如现有初始化器)的attached宏角色。Attached成员宏(MemberMacro)仅能添加新的成员(属性、方法、初始化器等),无法修改或替换已存在的自定义初始化器。

2. 实现支持新成员的初始化方法的方案

既然无法修改原有初始化器,我们可以通过添加新的初始化器来实现需求,同时保留原有初始化器的可用性(因为新增的count属性带有默认值0)。

具体实现示例

目标效果

使用@Counted宏后,结构体将同时支持两种初始化方式:

  • 原有方式:Item(name: "Johnny Appleseed")(count默认值为0)
  • 新方式:Item(name: "Johnny Appleseed", count: 5)

宏处理后的代码

struct Item {
    var name: String
    var count: Int = 0

    init(name: String) {
        self.name = name
    }

    // 宏自动添加的初始化器
    init(name: String, count: Int) {
        self.name = name
        self.count = count
    }
}

宏的核心实现代码

通过MemberMacro角色,我们同时添加count属性和新的初始化器:

import SwiftSyntax
import SwiftSyntaxMacros
import SwiftSyntaxBuilder

public enum CountedMacro: MemberMacro {
    public static func expansion(
        of node: AttributeSyntax,
        providingMembersOf declaration: some DeclGroupSyntax,
        in context: some MacroExpansionContext
    ) throws -> [DeclSyntax] {
        // 1. 添加count属性
        let countProperty = DeclSyntax(
            """
            var count: Int = 0
            """
        )
        
        // 2. 添加带count参数的初始化器
        let customInitializer = DeclSyntax(
            """
            init(name: String, count: Int) {
                self.name = name
                self.count = count
            }
            """
        )
        
        return [countProperty, customInitializer]
    }
}

// 注册宏
public struct CountedMacroPlugin: MacroPlugin {
    public static let providingMacros: [String: Macro.Type] = [
        "Counted": CountedMacro.self
    ]
}

通用化扩展(适配任意结构体)

如果希望@Counted宏能适配任意结构体,可通过SwiftSyntax遍历结构体的现有存储属性,动态生成包含所有原有属性和count的初始化器:

public enum CountedMacro: MemberMacro {
    public static func expansion(
        of node: AttributeSyntax,
        providingMembersOf declaration: some DeclGroupSyntax,
        in context: some MacroExpansionContext
    ) throws -> [DeclSyntax] {
        guard let structDecl = declaration as? StructDeclSyntax else {
            throw MacroError.onlyApplicableToStruct
        }
        
        // 获取结构体所有存储属性的名称和类型
        let storedProperties = try structDecl.memberBlock.members.compactMap { member -> (name: String, type: TypeSyntax)? in
            guard let varDecl = member.decl as? VariableDeclSyntax,
                  varDecl.bindingSpecifier.text == "var",
                  let binding = varDecl.bindings.first,
                  let idPattern = binding.pattern as? IdentifierPatternSyntax,
                  let typeAnnotation = binding.typeAnnotation else {
                return nil
            }
            return (idPattern.identifier.text, typeAnnotation.type)
        }
        
        // 生成初始化器参数列表
        let paramList = storedProperties.map { "\($0.name): \($0.type)" }
            .joined(separator: ", ")
        let paramWithCount = paramList.isEmpty ? "count: Int" : "\(paramList), count: Int"
        
        // 生成初始化器内部赋值语句
        let assignStatements = storedProperties.map { "self.\($0.name) = \($0.name)" }
            .joined(separator: "\n")
        let fullBody = assignStatements.isEmpty ? "self.count = count" : "\(assignStatements)\nself.count = count"
        
        // 构建初始化器
        let customInitializer = DeclSyntax(
            """
            init(\(raw: paramWithCount)) {
                \(raw: fullBody)
            }
            """
        )
        
        // 添加count属性
        let countProperty = DeclSyntax("var count: Int = 0")
        
        return [countProperty, customInitializer]
    }
}

enum MacroError: Error {
    case onlyApplicableToStruct
}

这样,无论结构体原有哪些属性,@Counted宏都会自动添加count属性和包含所有原有属性+count的初始化器,同时原有自定义初始化器依然可用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 19:16:28