【问题标题】:How to do "Deep Copy" in Swift?如何在 Swift 中进行“深度复制”?
【发布时间】:2014-09-05 10:24:19
【问题描述】:

在 Objective-C 中,可以通过以下方式进行深度复制:

 Foo *foo = [[Foo alloc] init];
 Foo *foo2 = foo.copy;

如何在 Swift 中进行这种深拷贝?

【问题讨论】:

标签: swift deep-copy


【解决方案1】:

如果Foo 是一个实现了NSCopying 的Objective-C 类,那么以下将起作用:

var foo2 = foo.copy();

-copy 在 Foundation 中没有使用属性表示法定义,因此即使您可以在 Objective-C 中使用点表示法,您也不能将其视为 Swift 中的属性。事实上,你真的不应该使用点表示法(即使它在语法上是合法的),因为-copy 在逻辑上不是对象的属性,它是一种不带参数的方法,它会生成对象的副本。

注意,这不是深拷贝,就像在 Objective-C 中它不是深拷贝一样,除非实现还复制 Foo 实例的所有成员。

【讨论】:

  • 您当然特别提到了Objective-C。 [myObject copy] 在那里是正确的,在 Swift 中是 myObject.copy()
  • 这实际上应该是对 OP 的正确答案。使用 copy() 和 NSCopying 是正确的方法。对于数组或字典,您需要遍历所有元素并自己制作副本。
【解决方案2】:

深拷贝

您的示例不是 StackOverflow 上讨论的 a deep copy。获取对象的真正深层副本通常需要NSKeyedArchiver

Swift 和复制

NSCopying 协议是 Objective-C 提供对象副本的方式,因为一切都是指针,您需要一种管理任意对象副本生成的方法。对于 Swift 中的任意对象副本,您可能会提供一个方便的初始化程序,您可以在其中初始化 MyObject 另一个 MyObject 并在 init 中将旧对象的值分配给新对象。老实说,这基本上是 -copy 在 Objective-C 中所做的,只是它通常必须在每个子对象上调用 copy,因为 Objective-C 实施防御性复制。

let object = MyObject()
let object2 = MyObject(object)

几乎所有东西都是按值传递的。差不多了。

然而,在 Swift almost everything is pass-by-value 中(你真的应该点击上述链接),因此对 NSCopying 的需求大大减少了。在 Playground 中尝试一下:

var array = [Int](count: 5, repeatedValue: 0)
print(unsafeAddressOf(array), terminator: "")
let newArray = array
print(unsafeAddressOf(newArray), terminator: "")
array[3] = 3
print(array)
print(newArray)

你可以看到赋值不是指针的副本,而是一个新的数组。对于围绕 Swift 的非按值复制语义与结构和类相关的问题的真正写得很好的讨论,我建议fabulous blog of Mike Ash

最后,如果您想从 Apple 那里听到您需要了解的所有信息,您可以观看the WWDC 2015 Value Semantics video。每个人都应该观看这个视频,它真正阐明了 Swift 中处理内存的方式以及它与 Objective-C 的不同之处。

【讨论】:

  • 如果您的数组包含对象(类实例),它们是“通过引用”,然后复制数组不会复制这些。您最终将得到 2 个指向相同对象的不同数组(数组是“按值”,因此它们确实被复制了)。
  • @HixField 如“几乎所有”链接文章中所述。
  • @HixField 这是一个很好的注释,应该在答案中突出显示。不阅读链接文章或阅读您的评论的 Swift 新手可能会错误地认为对象也会被复制到数组中。
  • 看起来不错,但为了避免无意中误导 Swift 新手,最好明确地合并 @HixField 的评论或一些变体。
  • @AllenLin 尝试导入 Foundation。
【解决方案3】:

致我健忘的未来自己:

对于正在寻找一种简单的方法来使用 Swift 对树型对象进行深度复制的其他人(父母可能/可能没有孩子,而那些孩子可能/可能没有孩子等等)......

如果您为 NSCoding 设置了类(用于启动之间的持久数据),我可以通过执行以下操作来使用该功能对该树结构的任何特定实例进行深度复制……

(斯威夫特 4)

class Foo: NSObject, NSCoding {
   var title = ""
   var children: [Foo] = []

   *blah, blah, blah*

   // MARK: NSCoding
   override public func encode(with coder: NSCoder) {
      super.encode(with: coder)
      coder.encode(title as Any?, forKey: "title")
      coder.encode(children as Any?, forKey: "children")
   }

   required public init?(coder decoder: NSCoder) {
      super.init(coder: decoder)
      self.title = decoder.decodeObject(forKey: "title") as? String ?? ""
      self.children = decoder.decodeObject(forKey: "children") as? [Foo] ?? []
   }

}

同时……在其他地方……

// rootFoo is some instance of a class Foo that has an array of child Foos that each can have their own same array

// Archive the given instance
let archive = NSKeyedArchiver.archivedData(withRootObject: rootFoo)

// Unarchive into a new instance
if let newFoo = NSKeyedUnarchiver.unarchiveObject(with: archive) as? Foo {

   // newFoo has identical copies of all the children, not references

}

【讨论】:

  • 如果使用 Swift 4,为什么不改用新的Codable
  • @LukeRogers 在这里相同,我猜在 Codable 的情况下,我们不必提供实现“编码(带编码器:NSCoder)或初始化?(编码器解码器:NSCoder)”
【解决方案4】:

基于之前的回答here

截至 2021 年 2 月,此问题尚无适当的解决方案。不过我们有很多解决方法。

这是我一直在使用的,我认为限制较少的一个。

  1. 让你的类符合可编码的要求
class Dog: Codable{
    var breed:String = "JustAnyDog"
}
  1. 创建这个帮助类
class DeepCopier {
    //Used to expose generic 
    static func Copy<T:Codable>(of object:T) -> T?{
       do{
           let json = try JSONEncoder().encode(object)
           return try JSONDecoder().decode(T.self, from: json)
       }
       catch let error{
           print(error)
           return nil
       }
    }
}
  1. 当您需要真正的深拷贝对象时调用此方法,如下所示:
 //Now suppose  
 let dog = Dog()
 guard let clonedDog = DeepCopier.Copy(of: dog) else{
    print("Could not detach Dog")
    return
 }
//Change/mutate object properties as you want
 clonedDog.breed = "rottweiler"

如您所见,我们使用 Swift 的 JSONEncoder 和 JSONDecoder,使用 Codable 的强大功能,无论我们的对象下有多少嵌套对象,都可以进行真正的深度复制。只需确保您的所有类都符合 Codable。

虽然它不是理想的解决方案,但它是最有效的解决方法之一。

【讨论】:

    猜你喜欢
    • 2013-12-03
    • 2018-10-08
    • 2018-07-30
    • 2011-09-25
    • 1970-01-01
    • 1970-01-01
    • 2019-10-22
    • 2016-03-13
    • 1970-01-01
    相关资源
    最近更新 更多