允许复制 Swift 类或结构体,同时更改任意字段。
@CopyWithChanges
宏可以应用于结构体或类,以生成一个函数,该函数接收可选参数,每个参数对应目标的一个字段,并返回一个新的实例,该实例是原始实例的副本,除了提供的参数之外。 它支持可选类型。
有时需要结构体的副本,但一个或多个字段存在差异。 正确的做法是使用每个字段的预期值调用 init
,但这很笨拙。 通常发生的情况是,必须更改的字段被设置为可变,进行赋值复制,然后在复制后进行更改。
// original
let s1 = LargeStruct(a: 5, b: 2, c: 3, d: 7, e: 4, f: 1, g: 6)
// cumbersome copy changing b to 2
let s2 = LargeStruct(a: s1.a, b: 2, c: s1.c, d: s1.d, e: s1.e, f: s1.f, g: s1.g)
// reasonably simple copy + change, but requires that s3 AND b be mutable even if neither is changed outside this context
var s3 = s1
s3.b = 2
使实例可变是一个小的权衡,甚至可以通过使用初始块的某种变体来解决。 但是,仅仅为了方便初始化而使字段可变,可以被视为一种严重的反模式。
为了避免使字段可变的需求,@CopyWithChanges
以 func with(...)
的形式提供所需的样板代码,它处理调用 init
时同时使用更改后的值和保留的值的样板代码。
// the solution proposed here
let s4 = s1.with(b: 2)
// the same, but changing two fields
let s5 = s1.with(c: 4, g: 7)
// will change f to nil (if field f is defined as Optional)
let s6 = s1.with(f: nil)
完整的行为是
nil
与不在调用中包含该字段相同,即,将使用原始结构体中的值;nil
将使该字段为 nil
;.some(nil)
与不在调用中包含该字段相同,即,将使用原始结构体中的值。该宏也可以应用于类。 在类或结构体中,都必须有一个逐个成员的 init
。 在结构体中,除非在结构体声明中定义了自定义 init,否则编译器会自动合成此初始化器。 也可以通过 Xcode 的自动完成功能生成它。
使用 @CopyWithChanges
宏注释目标类型以生成 func with(...) -> Self
import CopyWithChanges
@CopyWithChanges
struct Report {
let venue: String
let sponsor: String?
let drinks: [String]
let complexStructure: [Date: [(String, Int)]]
let characters: [String]?
let budget: Double
}
扩展后的代码
struct Report {
let venue: String
let sponsor: String?
let drinks: [String]
let complexStructure: [Date: [(String, Int)]]
let characters: [String]?
let budget: Double
public func with(venue: String? = nil, sponsor: String?? = .some(nil), drinks: [String]? = nil, complexStructure: [Date: [(String, Int)]]? = nil, characters: [String]?? = .some(nil), budget: Double? = nil) -> Self {
Self (
venue: venue ?? self.venue,
sponsor: sponsor == .none ? nil : self.sponsor,
drinks: drinks ?? self.drinks,
complexStructure: complexStructure ?? self.complexStructure,
characters: characters == .none ? nil : self.characters,
budget: budget ?? self.budget
)
}
}
Swift Package Manager 是一种用于自动化 Swift 代码分发的工具,并且集成到 swift
编译器中。
设置好 Swift 包后,添加 CopyWithChanges
作为依赖项就像将其添加到 Package.swift
的 dependencies
值中一样容易。
.package(url: "https://github.com/entonio/CopyWithChanges/tree/main", from: "1.1.0"),
然后,您可以通过将 CopyWithChanges
模块产品作为依赖项添加到您选择的 target
的 dependencies
值中,来将其添加到 target
中。
.product(name: "CopyWithChanges", package: "CopyWithChanges"),
此包中的所有文件均为 NOTICE 文件中提到的包贡献者的版权所有,并根据 Apache 2.0 许可证获得许可,该许可证允许商业用途。