Reputation: 7
What is the difference between deep copy and shallow copy on iOS world?? Please answer to this question very detailized!!
e.g a piece of code
class A {
var name: String
init(name: String) {
self.name = name
}
}
var a1 = A(name:"Yura")
var a2 = a1 // It is a shallow copy, isnt it?)
If we use .copy() method, we will receive deep copy, won't we?
But conceptually which main differences do they have? And isn't there possability to implement deep and shallow copies in other ways?
Upvotes: -1
Views: 1619
Reputation: 344
The way your question is formulated with the accompanying code is not really about copying but rather about reference vs value semantics.
A shallow or a deep copy is related to types adopting reference semantics (i.e. classes).
In Swift there is the NSCopying
protocol that is usually adopted and conformed to in order to define how a class type shall perform a copy.
For example this class always performs a deep copy of its properties:
final class Bar: NSCopying {
private(set) var value: Int
func increment() {
value += 1
}
init(value: Int = 0) { self.value = value }
func copy(with zone: NSZone? = nil) -> Any {
Self(value: self.value)
}
}
Bar
always performs a deep copy: it returns a new instance initialised with the same value of the original instance property. Such property is of type Int
, which adopts value semantics, hence:
let original = Bar(value: 0)
let clone = original.copy() as! Bar
if original !== clone { print("Different instances") } else { print("Same instance") }
// prints: "Different instances"
clone.increment()
print(original.value)
// prints: 0
print(clone.value)
// prints: 1
What happens if we create another class which has one of its properties of type Bar
? How do we define the way such property is gonna be copied?
Here comes into play shallow vs deep copy strategy:
final class Foo: NSCopying {
let name: String
let bar: Bar
init(name: String, initialValue: Int) {
self.name = name
self.bar = Bar(value: initialValue)
}
private init(name: String, bar: Bar) {
self.name = name
// we just assign the bar reference here…
self.bar = bar
}
func copy(with zone: NSZone?) -> Any {
// …we won't get a deep copy but a shallow copy instead!!!
Self(name: self.name, bar: self.bar)
}
}
In this case we performed a shallow copy of the property bar
. That's cause in the copy method we create a new instance of Foo
, but we initialise it with the same reference to the Bar
instance stored at bar
property:
let original = Foo(name: "George", initalValue: 0)
let clone = original.copy() as! Foo
if original !== clone { print("Different instances") } else { print("Same instance") }
// prints: "Different instances"
if original.bar !== clone.bar { print("Different bar instances") } else { print("Shared bar instance") }
// prints: "Shared bar instance"
original.bar.increment()
print(original.bar.value)
// prints: 1
print(clone.bar.value)
// prints: 1
As you may see here the clone instance of type Foo
got the value of its bar property also mutated as side effect of mutating the original Foo
instance.
To avoid this behaviour we could have leveraged on Bar
NSCopying
implementation so to obtain a copy of its bar
property:
final class Deep: NSCopying {
let name: String
let bar: Bar
init(name: String, initialValue: Int) {
self.name = name
self.bar = Bar(value: initialValue)
}
private init(name: String, bar: Bar) {
self.name = name
// We are assigning a copy of the bar parameter…
self.bar = bar.copy() as! Bar
}
func copy(with zone: NSZone?) -> Any {
// …Therefore we really perform a deep copy!
Self(name: self.name, bar: self.bar)
}
}
Now if we were to mutate a copy, we wouldn't get the side effect of also mutating the original and vice-versa:
let original = Deep(name: "George", initialValue: 0)
let clone = original.copy() as! Deep
clone.bar.increment()
print(original.bar.value)
// prints: 0
print(clone.bar.value)
// prints: 1
Here if Bar
was immutable, it didn't matter if we made a shallow copy of a property of this type, because there wouldn't be side effects: indeed it would have been better for the memory footprint of our application.
On the other hand, since Bar
was implemented as a mutable type, then we had to take into account possible side effects of its mutability in another reference having an internal property of this type.
Upvotes: 0
Reputation: 4111
Deep copy: we make deep copies, source (personObj) and destination(personObjAnother) objects have their own copies. Changes made to the newly copied objects does not impact source object.
//A person structure with variables personName and personAge
struct Person{
var personName : String?
var personAge : Int?
}
//Lets use the Person struct
//lets create person object
var personObj = Person()
//lets set person properties
personObj.personName = "Alok"
personObj.personAge = 18
//lets create a another person object and copy personObj to personObjAnother object.
var personObjAnother = personObj
//lets set personObjAnother properties
personObjAnother.personName = "Naitvik"
personObjAnother.personAge = 3
//lets print personObj
print(personObj.personName!) //prints "Alok"
print(personObj.personAge!) //prints 18
//lets print personObjAnother
print(personObjAnother.personName!) //prints "Naitvik"
print(personObjAnother.personAge!) //prints 3
Example of Shallow Copy: When we make shallow copies, source (personObj) and destination(personObjAnother) objects have shared copies. Changes made to the newly copied objects does also impact source object.
class PersonC{
var personName : String?
var personAge : Int?
}
//lets create person object
let personObj = PersonC()
//lets set person properties
personObj.personName = "Alok"
personObj.personAge = 18
//lets create a another person object and assign personObj.
let personObjAnother = personObj
//lets set personObjAnother properties
personObjAnother.personName = "Naitvik"
personObjAnother.personAge = 3
//lets print personObj
print(personObj.personName!) //prints "Naitvik"
print(personObj.personAge!) //prints 3
//lets print personObjAnother
print(personObjAnother.personName!) //prints "Naitvik"
print(personObjAnother.personAge!) //prints 3
Creating Deep Copies Of Reference Types:
//A personD class with variables personName and personAge
//We have to confirm NSCopying protocol and implement func copy(with zone: NSZone? = nil) -> Any
class PersonD : NSCopying{
var personName : String?
var personAge : Int?
func copy(with zone: NSZone? = nil) -> Any {
let copy = PersonD()
copy.personName = self.personName
copy.personAge = self.personAge
return copy
}
}
//lets create person object let personObj = PersonD()
//lets set person properties
personObj.personName = "Alok"
personObj.personAge = 18
/*lets create a another person object and assign personObj.
we will use copy method now to perform deep copy.
we have to make sure PersonD confirms to NSCopying protocol
and implements func copy(with zone: NSZone? = nil) -> Any*/
let personObjAnother = personObj.copy() as! PersonD
//lets set personObjAnother properties
personObjAnother.personName = "Naitvik"
personObjAnother.personAge = 3
//lets print personObj
print(personObj.personName!) //prints "Alok"
print(personObj.personAge!) //prints 18
//lets print personObjAnother
print(personObjAnother.personName!) //prints "Naitvik"
print(personObjAnother.personAge!) //prints 3
Here is the reference from my own website.
Upvotes: 1