如何在Swift中执行“Deep Copy”?

时间:2014-07-15 09:29:00

标签: swift deep-copy

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

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

如何在Swift中进行深层复制?

4 个答案:

答案 0 :(得分:18)

深层复制

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

迅捷并复制

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

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

几乎所有东西都是按值传递的。几乎。

但是,在Swift中almost everything is pass-by-value你应该真的点击前面提到的链接),因此对NSCopying的需求大大减少了。在游乐场试试这个:

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的区别。

答案 1 :(得分:7)

如果Foo是实现NSCopying的Objective-C类,则以下内容将起作用:

var foo2 = foo.copy();

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

请注意,这不是深层副本,因为它不是Objective-C中的深层副本,除非该实现还复制了Foo实例的所有成员。

答案 2 :(得分:7)

我遗忘的未来自我:

对于其他任何寻找简单方法用Swift进行树型对象深度复制的人(父母可能/可能没有孩子,而这些孩子可能/可能没有孩子等等)...

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

Swift 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

}

答案 3 :(得分:2)

基于之前的答案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。

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