toast toast - 24 days ago 6
Objective-C Question

How to make a copy of a Core Data object, that remains untouched

Core Data, automatically updating changed objects which is causing problems, and making things more confusing than it needs to be.

Say I have this bit of code:

let fetchRequest = Track.fetchRequest()

//update it
do {
let tracksFound = try self.managedObjectContext.fetch(fetchRequest) as! [Track]
print("retrieved")

let trackToUpdate = tracksFound[0]

trackToUpdate.locality = "please dont have updated"

do {
try self.managedObjectContext.save()
print("saved")
}
catch {
fatalCoreDataError(error)
}

for t in tracksFound {
print(t.locality)
}
}
catch {
fatalCoreDataError(error)
}


You can see it fetches a
[Track]
object array, then it updates the first elements properly
locality
with
please dont have updated
. Then it saves this object. Finally it reads through the
tracksFound
array that was defined at the beginning. I had expected
tracksFound
to remain untouched, the object
tracksFound[0].locality
has changed to
please dont have updated
.

How do I stop Core Data from updating my object? I basically want to make a copy of
tracksFound
that will remain untouched, so I can use it for decision making later on.

Thanks

Answer

As mentioned in Michael's comment, trackToUpdate is a reference to an instance of NSManagedObject. Different references pointing to the same object, … points to the same object. So there is no way to change one object and leave the other one untouched, because there is no other one. Swift obfuscates references by intention, whoever had that really, really brillant idea.

As you say, you have to create a copy. NSManagedObject does not implement the NSCopying protocol. There are good reasons for it: If the instance refer to other instances (relation), you have to decide, whether you copy them, too. Doing so, would lead to the danger of copying of the whole graph. Not doing so, takes you back to the original problem, that you share an referred instance. You have to make a decision.

It might be better to copy the properties into a simple dictionary. Please keep in mind, that creating a new instance, this becomes a part of the object graph. It is a kind of a code smell (even the term might be too hard) to have "short time" managed objects.

However, you can create a new instance. Then you get the entity description of the object via -entity. The entity description has a property -properties, that contains a list of all properties. Using this you can iterate over the source properties and store it into the new instance. Thanks to Objective-C's key-value coding, it is possible to do that at runtime.