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 thestruct
/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, passingtrue
for theflag
parameter [ofinit(array:copyItems:)
] will perform an immutable copy of the first level below the surface. If you passfalse
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
How to Perform an Action Only After Data Are Downloaded from Firebase
How to Init a Uibutton Subclass
How to Use Alamofire with Custom Headers
Add Subtitle Under the Title in Navigation Bar Controller in Xcode
How to Get Line Number and Function Name in Swift Language
Re-Initialize a Lazy Initialized Variable in Swift
Mathematical Functions in Swift
Pull Down to Refresh Data in Swiftui
Uiapplication.Shared.Delegate Equivalent for Scenedelegate Xcode11
Why Is There No Universal Base Class in Swift
Access Input from Uialertcontroller
What Is the Use of "Static" Keyword If "Let" Keyword Used to Define Constants/Immutables in Swift
What Are the Supported Swift String Format Specifiers
How to Print Out the Method Name and Line Number in Swift
Does Swift Implement Tail Call Optimization? and in Mutual Recursion Case
Spritekit Physics in Swift - Ball Slides Against Wall Instead of Reflecting