如何在Swift中执行"Deep Copy"?

all*_*nli 20 deep-copy swift

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

 Foo *foo = [[Foo alloc] init];
 Foo *foo2 = foo.copy;
Run Code Online (Sandbox Code Playgroud)

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

Cam*_*mer 18

深拷贝

您的示例不是StackOverflow中讨论的深层副本.获取对象的真正深层副本通常需要NSKeyedArchiver

迅捷和复制

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

let object = MyObject()
let object2 = MyObject(object)
Run Code Online (Sandbox Code Playgroud)

几乎所有东西都是价值传递.几乎.

但是,在Swift中,几乎所有东西都是按值传递的(你应该真的点击上面提到的链接),因此对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)
Run Code Online (Sandbox Code Playgroud)

您可以看到赋值不是指针的副本,而是实际上是一个新数组.对于围绕Swift的非复制值语义与结构和类相关的问题进行真正精心编写的讨论,我建议使用Mike Ash精彩博客.

最后,如果您想听到Apple需要了解的所有内容,您可以观看WWDC 2015 Value Semantics视频.每个人都应该观看这个视频,它确实清除了Swift中处理内存的方式以及它与Objective-C的区别.

  • 如果您的数组包含对象(类实例),它们是"通过引用"然后复制数组将不会复制这些对象.您将最终得到2个不同的数组(数组是"按值",因此它们确实被复制)指向相同的对象. (12认同)
  • @HixField这是一个很好的说明,应该在答案中突出显示.不读取链接文章或阅读评论的快速新手可能会错误地认为对象也会被复制到数组中. (4认同)
  • 看起来不错,但为了避免无意中误导Swift新手,明智地合并@HixField的评论或一些变化是理想的. (3认同)

小智 8

为了我的遗忘自我:

对于其他任何寻找一种简单的方法来使用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] ?? []
   }

}
Run Code Online (Sandbox Code Playgroud)

同时......在其他地方......

// 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

}
Run Code Online (Sandbox Code Playgroud)

  • 如果使用Swift 4,为什么不实现新的`Codable`呢? (3认同)

Jer*_*myP 7

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

var foo2 = foo.copy();
Run Code Online (Sandbox Code Playgroud)

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

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


shr*_*hah 5

基于以前的答案here

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

这是我一直在使用的一种,在我看来限制较少的一种。

  1. 使您的类符合可编码
class Dog: Codable{
    var breed:String = "JustAnyDog"
}
Run Code Online (Sandbox Code Playgroud)
  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
       }
    }
}
Run Code Online (Sandbox Code Playgroud)
  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"
Run Code Online (Sandbox Code Playgroud)

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

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