Swift Package Manager Platforms: macOS, iOS, tvOS, watchOS, Linux

ModifiedCopyMacro

一个 Swift 宏,用于通过修改属性来创建结构体的内联副本。
语法类似于 Kotlin 数据类的 copy 函数:https://kotlinlang.org/docs/data-classes.html#copying

用法

@Copyable 宏应用于结构体

@Copyable
struct Person {
    let name: String
    var age: Int
}

它将为每个存储属性和常量添加一个 copy 函数

struct Person {
    let name: String
    var age: Int

    /// Returns a copy of the caller whose value for `name` is different.
    func copy(name: String) -> Self {
        .init(name: name, age: age)
    }
    
    /// Returns a copy of the caller whose value for `age` is different.
    func copy(age: Int) -> Self {
        .init(name: name, age: age)
    }
}

功能、限制和设计选择

用于多个更改的链式调用

要创建结构体的副本并修改多个属性,您可以像这样链式调用 copy
Person(name: "Walter White", age: 50).copy(age: 52).copy(name: "Heisenberg")

这与 Kotlin 的 copy 版本不同,后者允许在单个调用中传递多个参数。
在 Swift 中不可能像那样实现,因为无法为参数设置默认值,而这些默认值是指结构体(或类)的属性的当前值。
我们也不能使用 nil 作为旧值/当前值的标记,因为 nil 可能是一个有效的新值,我们希望在创建副本时将属性设置为该值。
可能还有我不知道的方法来实现它。所以如果你知道怎么做,请告诉我。

CopyableCombi

在 2.1.0 版本中,引入了单独的宏 CopyableCombi,它会生成具有所有参数组合的 copy 函数。这种解决方案的缺点是生成的函数数量会很快变得很大,但它提供了一个更类似于 Kotlin copy 函数的 API。

存储属性和常量

将为结构体的每个存储属性(var)和每个常量(let)生成一个 copy 函数。
该宏通过检查计算属性是否具有 getset 访问器来识别它们。

仅适用于结构体

此宏仅适用于结构体。
对于枚举没有意义,因为枚举不能有存储属性。
类和 actor 具有引用语义,我不希望此库为引用类型提供 copy 函数。我只想通过修改的属性来增强结构体的自然复制能力。
当您尝试将其应用于结构体以外的任何内容时,此宏会发出诊断消息。

仅适用于默认 init

生成的 copy 函数通过调用合成的默认初始化器来生成副本。
因此,如果您提供自定义初始化器,则不会合成默认初始化器,并且 copy 函数将无法工作。
如果您在扩展中定义自定义初始化器,您仍然可以拥有合成的默认初始化器。

没有结构体扩展

您不能将此宏应用于结构体的扩展。
这似乎是宏系统的局限性。
如果您知道如何实现它,请告诉我 :)

安装

宏是 Swift 5.9 的一项新语言功能,它仅适用于 Xcode 15 及更高版本。

将 url https://github.com/WilhelmOks/ModifiedCopyMacro.git 作为 Swift Package 添加到您的项目。

出现提示时,选择要添加到目标的 Package Product ModifiedCopy(类型:库)。

进行 clean build。

在您要附加 @Copyable 宏的文件中 import ModifiedCopy