UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: What's the purpose of deep copies for classes?

Forums > 100 Days of SwiftUI

Hello! I was taking a look at some code and I was wondering if anyone knew the actual purpose of deep copies.

Here's some code that basically allows you to use a class (a reference type) as a value type:

class User {
    var username = "Anonymous"

    // A method to create a deep copy

    func copy() -> User {
        let user = User()
        user.username = username
        return user
    }
}

var user1 = User()
var user2 = user1.copy()

user2.username = "Taylor"

print(user1.username) // Anonymous
print(user2.username) // Taylor

This will allow me to copy user1 and create my own reference to data with user2. However, it seems like it's the exact same thing as creating an entirely new instance of User. I could just instantiate like this from the above code:

var user1 = User()
var user2 = User()

I would get the same print result. At this point, I could also just make this a struct:

struct User {
    var username = "Anonymous"
}

var user1 = User()
var user2 = User()

user2.username = "Taylor"

print(user1.username) // Anonymous
print(user2.username) // Taylor

I would get the same print result as well. These are just example pieces of code, but all of this is making me wonder when I would actually ever need to use a deep copy method. In this context, it doesn't seem very useful. Any thoughts?

As a bonus question, as you just saw the deep copy method is written like this:

// A method to create a deep copy

    func copy() -> User {
        let user = User()
        user.username = username
        return user
    }

However, I could also write it like this:

// A method to create a deep copy

    func copy() -> User {
        let user = User()
        return user
    }

I could do this, right? Creating a User already allows for the username to be set to "Anonymous". It seems repetitive.

Thanks! :)

2      

In this context it seems for me not very useful, either.

For example, if you have an object on which you want to do a lot of complicated and long lasting calculations (for whatever reason) you could do them on a deep copy and the original is untouched. This could be handy when you depend on network calls which usually could take a while. So your original is in a correct state all the time. When all is finished you can just reference the copy to your old variable and all is set and in a correct state.

Personally, I wasn't in the situation or had a use case to use a deep copy. But you know the possibility is there and if you need one, you know how to do it.

2      

I could do this, right? Creating a User already allows for the username to be set to "Anonymous". It seems repetitive.

Sure, but what if you're copying a User that has a different username than "Anonymous"?

var user1 = User()
user1.username = "Charlotte"

var user2 = user1.copy() //we create a new user named "Charlotte"

This is a rather simplistic case so the above could just as easily be accomplished like so:

var user1 = User()
user1.username = "Charlotte"

var user2 = User() //we create a new user named "Anonymous"
user2.username = user1.username //we change user2's username to "Charlotte"

but in a real-world case you would almost certainly have much more complicated and numerous properties, including perhaps properties of a data type that has its own properties and so on, and it would be a pain in the butt to have to do that with all of them. A copy method makes it much easier.

2      

Sure, but what if you're copying a User that has a different username than "Anonymous"?

@roosterboy Are you sure about that first statement? I just tested it now and it's doing what I believed it would do.

I have a User class without the extra line inside the method:

class User {
    var username = "Anonymous"

    func copy() -> User {
        let user = User()
        return user
    }
}

After that, I create user1 and a user2 copy:

var user1 = User()
user1.username = "Charlotte"
var user2 = user1.copy() // The username value would still be "Anonymous"

When I print the username for both user1 and user2:

print(user1.username) // Charlotte
print(user2.username) // Anonymous

Please correct me if I'm wrong, but I'm pretty sure I'm right.

I would also like to clarify what you mean here:

... but in a real-world case you would almost certainly have much more complicated and numerous properties, including perhaps properties of a data type that has its own properties and so on, and it would be a pain in the butt to have to do that with all of them. A copy method makes it much easier.

Perhaps I'm just tired, but I don't really understand your wording here. It would be a pain to instantiate classes compared to using a copy method? Do you mind if you rephrase/elaborate on this?

Thanks! :)

2      

@roosterboy Are you sure about that first statement? I just tested it now and it's doing what I believed it would do.

You are testing the code you wrote, not the original deep copy code. I was demonstrating that the original deep copy code doesn't set username to "Anonymous", as you implied it did here:

I could do this, right? Creating a User already allows for the username to be set to "Anonymous". It seems repetitive.

When you use the original copy method, you get back a User instance with the exact same property values as the original.

When you use your copy method, you get back a User instance with the default property values and have to manually change them to whatever the copied instance has.

I mean, with your code, you aren't making a copy of an existing User, you're creating a brand new User.

Basically, that user.username = username that you removed from the original has to then be performed outside the copy function in order to get an exact copy of the original User. That maybe fine for a single property, but what if you have multiple properties, some of which may be nested classes with their own properties?

Perhaps I'm just tired, but I don't really understand your wording here. It would be a pain to instantiate classes compared to using a copy method? Do you mind if you rephrase/elaborate on this?

Yeah, but it might not be until tomorrow; I don't have a lot of time to elaborate tonight.

2      

No worries, thanks for the help! I will also take a look at this tomorrow!

2      

Let's take the original code, with a copy method that makes a new User instance and sets its properties to the same values as the User instance being copied. And for purposes of example, we'll add a few more properties...

class User {
    var username = "Anonymous"
    var occupation = "Workerbee"
    var age = 20
    var bestFriend = "Somebody"

    func copy() -> User {
        let user = User()
        user.username = username
        user.occupation = occupation
        user.age = age
        user.bestFriend = bestFriend
        return user
    }
}

let user1 = User()
user1.username = "Charlotte"
user1.occupation = "Mystery Girl"
user1.age = 11
user1.bestFriend = "Shauna"
dump(user1)

let user2 = user1.copy()
dump(user2)

Our output looks something like this:

__lldb_expr_21.User #0
  - username: "Charlotte"
  - occupation: "Mystery Girl"
  - age: 11
  - bestFriend: "Shauna"
__lldb_expr_21.User #0
  - username: "Charlotte"
  - occupation: "Mystery Girl"
  - age: 11
  - bestFriend: "Shauna"

user2 is an exact copy of user1.

Now let's take your code, which has a copy method that just returns a new User with all values set to the default. In order to get a copy of user1, we try using the copy method just like we did before...

class User {
    var username = "Anonymous"
    var occupation = "Workerbee"
    var age = 20
    var bestFriend = "Somebody"

    func copy() -> User {
        let user = User()
        return user
    }
}

let user1 = User()
user1.username = "Charlotte"
user1.occupation = "Mystery Girl"
user1.age = 11
user1.bestFriend = "Shauna"
dump(user1)

let user2 = user1.copy()
dump(user2)

But our output looks something like this:

__lldb_expr_19.User #0
  - username: "Charlotte"
  - occupation: "Mystery Girl"
  - age: 11
  - bestFriend: "Shauna"
__lldb_expr_19.User #0
  - username: "Anonymous"
  - occupation: "Workerbee"
  - age: 20
  - bestFriend: "Somebody"

Clearly, user2 is not a copy of user1. So we have to do this to get the correct values for all the properties:

let user1 = User()
user1.username = "Charlotte"
user1.occupation = "Mystery Girl"
user1.age = 11
user1.bestFriend = "Shauna"
dump(user1)

let user2 = user1.copy()
user2.username = user1.username
user2.occupation = user1.occupation
user2.age = user1.age
user2.bestFriend = user1.bestFriend
dump(user2)

Now we have a copy of user1!

__lldb_expr_23.User #0
  - username: "Charlotte"
  - occupation: "Mystery Girl"
  - age: 11
  - bestFriend: "Shauna"
__lldb_expr_23.User #0
  - username: "Charlotte"
  - occupation: "Mystery Girl"
  - age: 11
  - bestFriend: "Shauna"

This is a very simple example. Just imagine what it would be like if User had a lot more properties and some of those properties had their own properties, etc.

And imagine what this would be like if you had several places in your code where you had to make a copy of a User instance. That would be at least one extra line for each property of User. Encapsulating the code that copies the values of the properties into a copy method makes things cleaner and less prone to error (because there's only one place in your code to update if you change the User class).

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.