swift deep-copy

¿Cómo hacer "Deep Copy" en Swift?



deep-copy (3)

En Objective-C, uno puede hacer una copia profunda de la siguiente manera:

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

¿Cómo hacer esto copia profunda en Swift?


Copia profunda

Su ejemplo no es una copia profunda como se explica en . Obtener una copia profunda de un objeto a menudo requeriría NSKeyedArchiver

Veloz y copiando

El protocolo NSCopying es la forma Objective-C de proporcionar copias de objetos porque todo era un puntero y necesitabas una forma de administrar la generación de copias de objetos arbitrarios. Para una copia de objeto arbitraria en Swift, puede proporcionar un inicializador de conveniencia donde inicialice MyObject en otro MyObject y, en el inicio, asigne los valores del objeto antiguo al nuevo objeto. Honestamente, eso es básicamente lo que hace la copia en Objective-C, excepto que generalmente tiene que llamar a copy en cada uno de los subobjetos, ya que Objective-C practica la copia defensiva.

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

Casi todo es pasado por valor. Casi.

Sin embargo, en Swift casi todo es pasado por valor ( realmente debería hacer clic en el enlace mencionado anteriormente ), por lo que la necesidad de NSCopying se reduce considerablemente. Prueba esto en un patio de juegos:

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)

Puede ver que la asignación no es una copia del puntero sino una nueva matriz. Para una discusión verdaderamente bien escrita de los temas que rodean la semántica de no copiar por valor de Swift en relación con las estructuras y clases, sugiero el fabuloso blog de Mike Ash .

Finalmente, si desea escuchar todo lo que necesita saber de Apple, puede ver el video de WWDC 2015 Value Semantics . Todo el mundo debería ver este video, realmente aclara la forma en que se maneja la memoria dentro de Swift y en qué se diferencia de Objective-C.


Si Foo es una clase de Objective-C que implementa NSCopying , lo siguiente funcionará:

var foo2 = foo.copy();

-copy no se define mediante la notación de propiedades en Foundation, por lo que no se puede tratar como una propiedad en Swift, aunque se puede usar la notación de puntos en Objective-C. De hecho, realmente no debería usar la notación de puntos (aunque es sintácticamente legal) porque -copy no es lógicamente una propiedad del objeto, es un método que no toma parámetros que fabrica una copia del objeto.

NB: esta no es una copia profunda, al igual que no es una copia profunda en Objective-C a menos que la implementación también copie a todos los miembros de la instancia de Foo.


A mi yo futuro olvidadizo:

Para cualquier otra persona que busque una manera fácil de hacer una copia en profundidad de un objeto de estilo de árbol con Swift (los padres pueden / no tener hijos y esos niños pueden / no pueden tener hijos, etc.)

Si tiene sus clases configuradas para NSCoding (para datos persistentes entre lanzamientos), pude usar esa capacidad para hacer una copia profunda de cualquier instancia particular de esta estructura de árbol haciendo lo siguiente ...

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

Mientras tanto ... en otra parte ...

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