这个简单的 Swift 库提供了 Cloneable
协议,允许你轻松创建任何遵守该协议的对象的深拷贝。 当你想复制引用类型的值,而不是共享同一个引用时,这非常有用。 因为它们有不同的引用,所以对原始对象的更改不会影响复制的对象,反之亦然。
要遵守 Cloneable
协议,只需在你的类型中实现 init(cloning: Self)
初始化器。 此初始化器应该执行对象的深拷贝。 有关深拷贝和浅拷贝之间差异的更多信息,请参阅 Geeks for Geeks 的这篇文章。
通过遵守 Cloneable
协议,你承诺你的实现将创建对象的深拷贝(而不仅仅是浅拷贝)。
为了清晰起见,让我们定义一些术语。 在 Swift 中,我们可以复制值或引用。 当我们复制一个值时,我们正在创建该值的新实例。 当我们复制一个引用时,我们正在创建对同一实例的新引用。 在此库的上下文中,每当我们使用术语“克隆”时,我们实际上指的是“深拷贝”。 这意味着我们正在复制引用类型的值,而不是引用本身。 我们正在创建对象的全新实例,而不仅仅是对同一对象的新引用。
假设我们有一个引用类型。 如果我们复制它,那么我们将在原始对象和复制的对象之间共享一个引用。 这意味着对原始对象的更改将会影响复制的对象,反之亦然。
class ReferenceType {
var int: Int
init(int: Int) {
self.int = int
}
}
var reference1 = ReferenceType(value: 1)
var reference2 = reference1
reference2.int = 2
print(reference1.int) // 2
print(reference2.int) // 2
为了避免这种情况,我们可以遵守 Cloneable
协议并实现 init(cloning: Self)
初始化器。
extension ReferenceType: Cloneable {
convenience init(cloning: ReferenceType) {
self.init(int: cloning.int)
}
}
var cloneOfReference1 = ReferenceType(cloning: reference1)
cloneOfReference1.int = 3
print(reference1.int) // 2
print(cloneOfReference1.int) // 3
通过遵守 Cloneable
协议,我们还会自动获得一个名为 clone()
的便利方法,该方法返回对象的深拷贝。
var cloneOfReference1 = reference1.clone()
通常,值类型是通过值复制,而不是通过引用复制。 这意味着当我们复制一个值类型时,我们已经在创建一个新的值实例。 但是,如果值类型包含引用类型,那么值类型的每个副本将共享对同一引用类型的引用。 为了避免这种情况,我们可以遵守 Cloneable
协议并实现 init(cloning: Self)
初始化器。
struct ValueType {
var referenceType: ReferenceType
}
extension ValueType: Cloneable {
init(cloning original: ValueType) {
self.referenceType = original.referenceType.clone()
}
}
同样重要的是要注意,仅仅因为您的值类型包含引用类型,并不意味着它会共享引用。 这是因为在 Swift 中,使用写时复制实现值语义是一种常见的模式。 换句话说,许多值类型已经具有内置机制来深度复制其引用类型属性。 (例如,Array
和 String
就是这样工作的。 在底层,它们保存对引用类型的引用,但在必要时会对它进行深拷贝。)
一般来说,除非您有特定的需要,否则将值类型遵守 Cloneable
协议没有好处,也不建议这样做。
如果您的类型遵守 Codable
,那么 Cloneable
协议将自动生成一个名为 cloneUsingCodable()
的方法,该方法使用 JSONEncoder
和 JSONDecoder
返回对象的深拷贝。 当您不想维护自己的 Cloneable
协议实现,并且只想让 Codable
“为您处理”时,此方法非常有用。 init(cloning: )
依赖于您来确保您的拷贝是递归深拷贝,而 cloneUsingCodable()
保证是深拷贝。 缺点是编码和解码可能会失败。 因此,该方法返回一个可选值。
guard let cloneOfReference1 = reference1.cloneUsingCodable() else {
// Handle error
}
如果您的类型遵守 NSCopying
或 NSMutableCopying
,那么 Cloneable
协议将分别自动生成一个名为 cloneUsingNSCopying()
或 cloneUsingNSMutableCopying()
的方法。 这些方法使用 NSCopying
和 NSMutableCopying
的 copy()
或 mutableCopy()
方法返回对象的深拷贝。 这些方法与普通的 NSCopying
和 NSMutableCopying
没有太大区别,但它们通过为您处理类型转换,增加了一层便利性。 缺点是这些方法返回一个可选值。
guard let cloneOfReference1 = reference1.cloneUsingNSCopying() else {
// Handle error
}
要使用 Swift Package Manager 安装 Cloneable,请将以下行添加到您的 Package.swift
文件中的 dependencies 数组
dependencies: [
.package(url: "", from: "1.0.0")
]
然后将以下行添加到你的 target 的 dependencies 数组
dependencies: [
.product(name: "Cloneable", package: "Cloneable")
]
该库是在 MIT 许可证下授权的。 有关更多信息,请参阅 LICENSE
。