How to Do "Deep Copy" in Swift

How to do Deep Copy in Swift?

Deep Copy

Your example is not a deep copy as discussed on StackOverflow. Getting a true deep copy of an object would often require NSKeyedArchiver

Swift and copying

The NSCopying protocol is the Objective-C way of providing object copies because everything was a pointer and you needed a way of managing the generation of copies of arbitrary objects. For an arbitrary object copy in Swift you might provide a convenience initializer where you initialize MyObject another MyObject and in the init assign the values from the old object to the new object. Honestly, that is basically what -copy does in Objective-C except that it usually has to call copy on each of the sub-objects since Objective-C practices defensive copying.

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

Almost everything is pass-by-value. Almost.

However, in Swift almost everything is pass-by-value (you should really click the aforementioned link) so the need for NSCopying is greatly diminished. Try this out in a 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)

You can see that the assignment is not a copy of the pointer but actually a new array. For a truly well-written discussion of the issues surrounding Swift's non-copy-by-value semantics with relation to structs and classes I suggest the fabulous blog of Mike Ash.

Finally, if you want to hear everything you need to know from Apple you can watch the WWDC 2015 Value Semantics video. Everyone should watch this video, it really clears up the way memory is handled within Swift and how it differs from Objective-C.

how to make a deep copy of a swift array of class objects

As a simple statement, you could use code like this:

var copiedArray = array.map{$0.copy()}

Note that the term "deepCopy" is a little misleading for what you're talking about. What if the array is heterogeneous and contains other containers like arrays, dictionaries, sets, and other custom container and "leaf" objects? What you should really do is to create a protocol DeepCopiable and make it a requirement that any object that conforms to DeepCopiable require that any child objects also conform to the DeepCopiable protocol, and write the deepCopy() method to recursively call deepCopy() on all child objects. That way you wind up with a deep copy that works at any arbitrary depth.

How does an array in swift deep copy itself when copied or assigned

Assignment of any struct (such as Array) causes a shallow copy of the structure contents. There's no special behavior for Array. The buffer that stores the Array's elements is not actually part of the structure. A pointer to that buffer, stored on the heap, is part of the Array structure, meaning that upon assignment, the buffer pointer is copied, but it still points to the same buffer.

All mutating operations on Array do a check to see if the buffer is uniquely referenced. If so, then the algorithm proceeds. Otherwise, a copy of the buffer is made, and the pointer to the new buffer is saved to that Array instance, then the algorithm proceeds as previously. This is called Copy on Write (CoW). Notice that it's not an automatic feature of all value types. It is merely a manually implemented feature of a few standard library types (like Array, Set, Dictionary, String, and others). You could even implement it yourself for your own types.

When CoW occurs, it does not do any deep copying. It will copy values, which means:

  • In the case of value types (struct, enum, tuples), the values are the struct/enum/tuples themselves. In this case, a deep and shallow copy are the same thing.
  • In the case of reference types (class), the value being copied is the reference. The referenced object is not copied. The same object is pointed to by both the old and copied reference. Thus, it's a shallow copy.

Copy command - deep or shallow copy?

The copy performs a shallow copy (a copy of the collection, but the objects in the array are not copied). E.g.

let obj1 = Object(value: 1)
let obj2 = Object(value: 2)
let obj3 = Object(value: 3)

let originalArray = [obj1, obj2, obj3] as NSArray
let copyArray = originalArray.copy() as! NSArray

print(String(format: "original address: %p", originalArray))

for obj in originalArray {
print(String(format: " %p", obj as! Object))
}

print(String(format: "copy address: %p", copyArray))

for obj in copyArray {
print(String(format: " %p", obj as! Object))
}

Note, I use an object other than NSNumber and NSString, as those have optimizations that may make looking at addresses misleading.

The member objects of these two arrays point to the same objects (i.e. a shallow copy).

original address: 0x618000044920
0x6180000277e0
0x618000027f00
0x618000027ea0
copy address: 0x618000044920
0x6180000277e0
0x618000027f00
0x618000027ea0

In fact, as you can see, because it's an immutable NSArray, it appears to optimize this where the copy is actually returning the same array instance. If you use mutable arrays, NSMutableArray, you'll see two unique arrays returned, as you'd expect, but they will still point to the same collection of objects.

But, if you use NSArray(array:copyItems:) with true for copyItems, you will get two unique arrays with unique copies of each member object:

let copyArray = NSArray(array: originalArray as! [Any], copyItems: true)

That yields a deep copy (two unique arrays where the individual member objects are copied and they, too, have unique addresses):

original address: 0x618000059e60
0x618000027ae0
0x618000028340
0x618000028280
copy address: 0x61800005ba20
0x618000028800
0x618000028aa0
0x618000028400

See the documentation for init(array:copyItems:) which says:

The copy(with:​) method performs a shallow copy. If you have a collection of arbitrary depth, passing true for the flag parameter [of init(array:copyItems:)] will perform an immutable copy of the first level below the surface. If you pass false the mutability of the first level is unaffected. In either case, the mutability of all deeper levels is unaffected.


Personally, though, if writing Swift, I'd generally use Array and Dictionary value types rather than the old NSArray and NSDictionary types.

deep copy for array of objects in swift

Since ordered is a swift array, the statement

 var orderedCopy = ordered

will effectively make a copy of the original array.

However, since Meal is a class, the new array will contain references
to the same meals referred in the original one.

If you want to copy the meals content too, so that changing a meal in one array will not change a meal in the other array, then you must define Meal as a struct, not as a class:

struct Meal { 
...

From the Apple book:

Use struct to create a structure. Structures support many of the same behaviors as classes, including methods and initializers. One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference.

Right way to implement deep copy for object which contains nested objects in Swift?

Assuming your filter choice also conforms:

let copy = ZLFilters(filterChoices: filterChoices!.copyWithZone(zone))


Related Topics



Leave a reply



Submit