Reputation: 5349
What is the best way to make a copy of a class
, not a struct
variable without making a reference to it's memory location.
For example
class Person {
var name: String = ""
var age: Int = 0
init(name: String, age: Int) {
self.name = name
self.age = age
}
var mapped: [String: Any] {
return [
"name": self.name,
"age": self.age
]
}
}
var person: Person = Person(name: "Hitchhiker", age: 42)
var personCopy = person
var dictionary: [String: Any] {
return [
"a_person": person.mapped,
"c_personCopy": personCopy.mapped
]
}
print("Original:", dictionary.jsonStringValue)
person.age = 100
person.name = "Guide"
print("\n\n========\n\n")
print("Edit 1:", dictionary.jsonStringValue)
personCopy.age = 200
personCopy.name = "To the Galaxy"
print("\n\n========\n\n")
print("Edit 2:", dictionary.jsonStringValue)
// I have this swift extension to make my logs better, don't mind the implicit unwrapping.
extension Dictionary {
var jsonStringValue: String {
let jsonData = try! JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
return String(data: jsonData, encoding: String.Encoding.utf8)!
}
}
This prints an output which looks like this.
Original: {
"a_person" : {
"name" : "Hitchhiker",
"age" : 42
},
"c_personCopy" : {
"name" : "Hitchhiker",
"age" : 42
}
}
========
Edit 1: {
"a_person" : {
"name" : "Guide",
"age" : 100
},
"c_personCopy" : {
"name" : "Guide",
"age" : 100
}
}
========
Edit 2: {
"a_person" : {
"name" : "To the Galaxy",
"age" : 200
},
"c_personCopy" : {
"name" : "To the Galaxy",
"age" : 200
}
}
If I manipulate the values of the copies, the originals will also be, updated because the copies are assigned by reference.
I know I can create something like an extension function which makes a copy
of the original variable, like this.
extension Person {
func copy() -> Person {
return Person(name: self.name, age: self.age)
}
}
var person = Person(name: "Jon Snow", age: 0)
var personCopy = person.copy()
personCopy.name = "Jon Targaryen" // this is now a value copy.
But how can I do this easier without creating a lot of boilerplate code for a lot of different models?
Update:
I know that I can use Swift Protocols here, for example
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
Which I saw in this answer, but I'd have to subclass my Model classes to these, which may cause some problems since my Model is already a subclass and it wants me to implement some of these boilerplate initializers and I don't want that
Upvotes: 2
Views: 1969
Reputation: 318774
Classes are reference types. The only way to get a copy is to define a copy
method as you have or to create an init
method that takes another instance to be copied (which is essentially the same as your copy
method).
You really should consider making your model classes as struct
instead of class
as long as you don't need inheritance. Then you get the copy semantics automatically.
Upvotes: 2