I have to check for certain properties after saving the object to the database (I need to make sure first that it's saved on disk). So, I thought that the didSave method of NSManagedObject is the best place to do so.
However, after checking for these properties and changing some of them, I want to re-save the object. So, I call the managed object context to save the object one more time. (I made heavy testing to make sure I won't get into an infinite loop).
Now, the problem is the managed object context doesn't perform the second save. How did I know that? Well, first I checked the hasChanged property of the context for the second save and it returns no. Also, the didSave method is not called one more time due to the re-save.
Is there something I am doing wrong? What's wrong with my algorithm?
Note:
I considered willSave at the beginning but as it turned out, willSave is called before validation. The object may not be saved on disk after all. I need to perform my check and new setting after saving to disk.
Related
I have RestKit 0.23.0 with Core Data and NSFetchedResultsController in my project. I want to change an attribute of an entity and reload my view in - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller.
I get the managed object that I want to change from objectManager.managedObjectStore.mainQueueManagedObjectContext, change the attribute and call objectManager.managedObjectStore.mainQueueManagedObjectContext saveToPersistentStore:&error]. The controllerDidChangeContentmethod is called, but the data is not written to the database at that time.
After debugging in NSManagedObjectContext+RKAdditions.m, I saw that success = [contextToSave save:&localError];is called two times. The first run fires controllerDidChangeContent (data is not written to the database at that time) and the second call writes the data to the database, but does not call controllerDidChangeContent.
Any ideas what I did wrong?
There is nothing 'wrong', you are just seeing 2 different stores being saved. First the main thread store (non-persistent) and then the persistent store (backed on disk). The main thread store is a child of the persistent store.
So, you get a notification when the main thread store is saved, because that is what you asked for, but the persistent store hasn't been saved yet so nothing has been saved to disk.
This should not cause you a problem. If it does you either have a logical error in your code or your approach to solving the problem isn't quite right.
I has a question that.
I need a runtime attribute in MyEntity, it is changed very offen.
And there are many MyEntity in core data.(such as 1000,0000);
I know that the transient attribute wont be saved in the disk, so these 1000,0000 MyEntities must be in memory all the time? but there are so many MyEntites,
the memory is large enough to keep 1000,0000 MyEntities?
If you need to change values on a large number of objects, those objects must exist. This is true whether or not you're using Core Data.
With Core Data there are various options for keeping memory under control-- getting rid of individual objects by re-faulting them, or getting rid of all managed objects by resetting the managed object context, for example. But it's hard to tell what you're really trying to do here and why any of this is needed. If this attribute is transient, why would you want to change it on an object that you're not using, that's not even loaded into memory? You could load the object, change the transient value, and then get rid of the object to keep memory use under control. But since the transient attribute doesn't get saved, what's the point? When you're done, nothing has changed. Why not just skip the update completely?
I've got a NSManagedObject in Context A and would like to have it in Context B. If I use the objectID to load it in Context B, all the data in context A is lost, so I would like to copy/move the data, something like
[managedObjectInContextA copyTo:contextB];
How can I achieve this?
Thanks a lot,
Stefan
Three steps:
early on, register somewhere appropriate for the NSManagedObjectContextDidSaveNotification. (The view controller that owns context B might be a good choice; it depends on your app architecture.)
Make changes to context A's copy of the object, and save context A. This causes the above notification to fire.
in the method that receives this notification, call mergeChangesFromContextDidSaveNotification: on context B. This causes context B to get in sync with the freshly saved changes.
Context B's copy of the object now has the new state.
If both copies of the object might have unsaved changes, things get more complicated. See NSManagedObjectContext's mergePolicy to see ways of handling that if you need to.
The most obvious solution would be to just save the data to the persistent store before switching contexts.
[managedObjectContextA save:&error];
I have a Silverlight app where I've implemented the M-V-VM pattern so my actual UI elements (Views) are separated from the data (Models). Anyways, at one point after the user has gone and done some selections and possible other input, I'd like to asyncronously go though the model and scan it and compile a list of optiions that the user has changed (different from the default), and eventually update that on the UI as a summary, but that would be a final step.
My question is that if I use a background worker to do this, up until I actually want to do the UI updates, I just want to read current values in one of my models, I don't have to synchronize access to the model right? I'm not modifying data just reading current values...
There are Lists (ObservableCollections), so I will have to call methods of those collections like "_ABCCollection.GetSelectedItems()" but again I'm just reading, I'm not making changes. Since they are not primitives, will I have to synchronize access to them for just reads, or does that not matter?
I assume I'll have to sychronize my final step as it will cause PropertyChanged events to fire and eventually the Views will request the new data through the bindings...
Thanks in advance for any and all advice.
You are correct. You can read from your Model objects and ObservableCollections on a worker thread without having a cross-thread violation. Getting or setting the value of a property on a UI element (more specifically, an object that derives from DispatcherObject) must be done on the UI thread (more specifically, the thread on which the DispatcherObject subclass instance was created). For more info about this, see here.
First, let me illustrate the steps to reproduce the 'bug'.
Create a new NSManagedObject.
Fault the managed object using refreshObject:mergeChanges:NO - At this time, the didTurnIntoFault notification is received by the object.
'Unfault' the object again by using willAccessValueForKey:nil - At this time, the awakeFromFetch notification is supposed to be received BUT NO NOTIFICATION COMES. All code relying of it firing fails, and the bread in the toaster burns :)
The interesting thing is that if I 'save' the managed object context before performing step 2, everything works okay and the awakeFromFetch notification comes as expected.
Currently the workaround that I am using is 'saving' the context at regular intervals, but that is more of a hack since we actually need to save the context once (when the application terminates).
Googling has so far returned nothing concrete, except a gentleman here that seems to have run into the same problem.
So my question is twofold - Is this really a bug, and if it is, then what other walkarounds (sic) do you suggest.
EDIT: THIS IS NOT A BUG BUT THAT WAS JUST ME BEING STUPID. See, if I turn an object to fault without saving it, then there is no history of the object to maintain. So in this case (i.e for an unsaved object) there is no logical concept of awakeFromFetch (since it was never saved). Please do let me know if I am still getting it all mixed up.
Anyways, turns out my 'actual' problem was somewhere else - hidden well behind 2 gotcha's
If you use refreshObject:mergeChanges:NO to turn an object to fault in order to break any retain cycles that core data might have established, you have to do the same for the child objects also - Each child object which might have gotten involved in a cyclic retain with someone else will have to be manually faulted. What I had (wrongly) assumed was that faulting the parent will automatically break the cycles amongst the children.
The reverseTransform function of your custom transformers will NOT be called when such a object (i.e. which has been forcefully faulted) is resurrected by firing a fault on it. This in my eyes IS a bug, since there is no other way for me to know when the object is alive again. Anyways, the workaround in this case was to set the staleness interval to an arbitrarily low value so that core data skips its cache and always calls the reverseTransform function to resurrect the object. Better suggestions are welcome.
it really has been one of those days :)