web-dev-qa-db-fra.com

Comment faire "Deep Copy" dans Swift?

Dans Objective-C, on peut copier en profondeur en suivant:

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

Comment faire cette copie profonde dans Swift?

20
allenlinli

Copie profonde

Votre exemple n'est pas ne copie complète comme discuté sur StackOverflow. Obtenir une véritable copie complète d'un objet nécessiterait souvent NSKeyedArchiver

Rapide et copiant

Le protocole NSCopying est le moyen Objective-C de fournir des copies d'objets car tout était un pointeur et vous aviez besoin d'un moyen de gérer la génération de copies d'objets arbitraires. Pour une copie d'objet arbitraire dans Swift vous pouvez fournir un initialiseur pratique où vous initialisez MyObject un autre MyObject et dans l'init affectez les valeurs de l'ancien objet au nouvel objet. Honnêtement, c'est essentiellement ce que -copy le fait dans Objective-C, sauf qu'il doit généralement appeler la copie sur chacun des sous-objets, car Objective-C pratique la copie défensive.

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

Presque tout est pass-by-value. Presque.

Cependant, dans Swift presque tout est pass-by-value ( vous devez vraiment cliquer sur le lien susmentionné ) donc le besoin de NSCopying est considérablement diminué. Essayez ceci dans un 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)

Vous pouvez voir que l'affectation n'est pas une copie du pointeur mais en fait un nouveau tableau. Pour une discussion vraiment bien écrite des problèmes entourant la sémantique de Swift sans copie par valeur en relation avec les structures et les classes, je suggère le fabuleux blog de Mike Ash .

Enfin, si vous voulez entendre tout ce que vous devez savoir de Apple vous pouvez regarder la vidéo WWDC 2015 Value Semantics . Tout le monde devrait regarder cette vidéo, cela clarifie vraiment la façon dont la mémoire est gérée dans Swift et comment elle diffère d'Objective-C.

18

À mon moi futur oublieux:

Pour tous ceux qui recherchent un moyen facile de faire une copie complète d'un objet de style arbre avec Swift (le parent peut/peut ne pas avoir d'enfants et ces enfants peuvent/peuvent ne pas avoir d'enfants, etc.) …

Si vous avez vos classes configurées pour NSCoding (pour les données persistantes entre les lancements), j'ai pu utiliser cette capacité pour faire une copie complète de toute instance particulière de cette arborescence en procédant comme suit…

(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] ?? []
   }

}

Pendant ce temps… Ailleurs…

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

}
7
Joniz

Si Foo est une classe Objective-C qui implémente NSCopying, alors ce qui suit fonctionnera:

var foo2 = foo.copy();

-copy n'est pas défini en utilisant la notation de propriété dans Foundation, vous ne pouvez donc pas la traiter comme une propriété dans Swift même si vous pouvez utiliser la notation par points dans Objective-C. En fait, vous ne devriez vraiment pas n'utilisez pas la notation par points (même si elle est syntaxiquement légale) parce que -copy n'est pas logiquement une propriété de l'objet, c'est une méthode ne prenant aucun paramètre qui fabrique une copie de l'objet.

NB ce n'est pas une copie complète, tout comme ce n'est pas une copie complète dans Objective-C à moins que l'implémentation copie également tous les membres de l'instance Foo.

7
JeremyP