Jean-Luc Godard
Jean-Luc Godard

Reputation: 1883

Copy command - deep or shallow copy?

When I am copying the object with copy command in iOS (swift / objective c), Will that be a shallow copy or a deep copy ?

var str = "Hello, playground"
var copyStr = str.copy()

and

var originalArray = [1, 2, 3, 4] as NSArray
var copyArray = originalArray.copy()

withUnsafePointer(to: &originalArray) {
    print(" originall address: \($0)")
}

withUnsafePointer(to: &copyArray) {
    print(" copy address: \($0)")
}

Help me with the lines above , is this a deep copy or a shallow copy ?

The issue is , this behaviour in objective c is showing me the same address with both string and array which is shallow copy and in swift3 it is showing me different address which is deep copy

Upvotes: 2

Views: 978

Answers (2)

Rob
Rob

Reputation: 437432

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.

Upvotes: 3

Mitesh Desai
Mitesh Desai

Reputation: 21

String in Swift are Value Types. For all Value Types, a new copy of the existing String value is created.

From Apple Document.

Swift’s copy-by-default String behavior ensures that when a function or method passes you a String value, it is clear that you own that exact String value, regardless of where it came from. You can be confident that the string you are passed will not be modified unless you modify it yourself.

Hope this helps

Upvotes: 0

Related Questions