I have this code:
Store* store = [NSEntityDescription insertNewObjectForEntityForName:#"Store"];
store.name = #"My Company"
...
Now the store is managed in the context and will be saved when the context is saved, but I have a button where the user can cancel the form where data is collected. How do I undo or remove this from the context? Or am I thinking wrong?
Core Data has built-in support for undo, so you can undo individual changes by sending the -undo message to the context:
[store.managedObjectContext undo];
It also supports -redo. You can undo all changes up to the most recent save using the -rollback method:
[store.managedObjectContext rollback]
as indicated in #melsam's answer.
As mentioned earlier, you can use an undo manager. Or, you could simply use a separate ManagedObjectContext, and do all your changes in there. If you decide to keep them, save the context. If not, simply discard it. A MOC is just a scratch pad for work, and has no impact on the underlying database until saved.
You can't really "detach an entity" but you can cause a managed object to turn back into a fault, losing any changes that have not been saved.
[managedObjectContext refreshObject:object mergeChanges:NO];
Snipped from the documentation...
If flag is NO, then object is turned into a fault and any pending
changes are lost. The object remains a fault until it is accessed
again, at which time its property values will be reloaded from the
store or last cached state.
[store.managedObjectContext rollback];
Undo works only when I create a undoManager(Swift 5):
managedObjectContext.undoManager = UndoManager()
After this configuration you can undo a last change:
managedObjectContext.undo()
Also you could save all data from the user in an array and when the user is ready, you only have to save the array to core data.
Related
I already have a tableView with data in it. IF you tap a cell/row it pushes to an edit type of view. Is there anyway to edit core data's data other than: By edit, i mean i already have data inserted into my context. I have loaded my data into my view, the user can change the existing data, and re save it.
.h
//Below is the entity/entity's class name 'Amm'
Amm *amm;
.m
-(IBAction)save
{
[self.amm setValue:self.nameField.text forKey:#"name"];
[self.amm setValue:self.nicknameField.text forKey:#"nickname"];
[self.navigationController popViewControllerAnimated:YES];
NSError *error;
if (![self.managedObjectContext save:&error]) {
//Handle Error
}
}
I want this code to work, however the design pattern of my app isnt allowing this code to work for me as it does in other parts of my app. Thank you very much for any and all help!
I assume from what you've said you have:
A table view listing your managed objects
A view where you can edit the values of a managed object
A save button bound to the save method
What's the actual issue? I'm assuming when you tap save that:
The values in self.nameField.text isn't setting self.amm.name
The values in self.nicknameField.text isn't setting self.amm.nickname
Is that right? If so perhaps try the following code to set the managed object values:
self.amm.name = self.nameField.text
self.amm.nickname = self.nicknameField.text
If that's not the issue and you are actually setting the managed object values properly, is it that you just need to refresh the table-view? Perhaps use some NSLog commands to log every step of the applications progress.
This bug has been busting me for the past 4 hours.
Also when I swap it round and get the User data first then Message data... the User.name will show, but the Message.message will not. So the data is definitely going in but the relationship between them seems to be broken.
Firstly, +1 for the effort with the image you created to illustrate your problem.
The cause of your issue is that you never assigned the user to the message (or vice-versa).
Try
message.fetchUser = user;
or
user.fetchMessage = message;
Then save your context and perform the fetch request.
As Rog said, I needed to assign the user to the message (or vice vera). Basically I placed this bit of code after where its entering the data.
messageDetails.fetchUser = userDetails
I am wondering if someone could clarify the following from Apples Core Data documentation:
Changes are not reflected until after the controller’s managed object
context has received a processPendingChanges message. Therefore, if
you change the value of a managed object’s attribute so that its
location in a fetched results controller’s results set would change,
its index as reported by the controller would typically not change
until the end of the current event cycle (when processPendingChanges
is invoked). For example, the following code fragment would log
“same”:
NSFetchedResultsController *frc = <#A fetched results controller#>;
NSManagedObject *managedObject = <#A managed object in frc's fetchedObjects array#>;
NSIndexPath *beforeIndexPath = [frc indexPathForObject:managedObject];
[managedObject setSortKeyAttribute:
<#A new value that changes managedObject's position in frc's fetchedObjects array#>;
NSIndexPath *afterIndexPath = [frc indexPathForObject:managedObject];
if ([beforeIndexPath compare:afterIndexPath] == NSOrderedSame) {
NSLog(#"same");
}
What exactly does "would typically not change until the end of the current event cycle" mean? I have this situation in my code but am not really sure if I can 100% rely on my indexes staying the same until I explicitely preform a save on my managed object context. Could the above code be modified, without performing a save to the context, such that it doesn't log "same"?
not really sure if I can 100% rely on my indexes staying the same
until I explicitely preform a save on my managed object context.
I wouldn't. The "current event cycle" is the current iteration of the run loop.
I'm currently using a singleton as a data store for my app. I essentially store a number of events that are pulled and parsed from a web service and then added as needed. Each time I make a request from the web service, I parse the results and see if the items already exist. If they do, I delete them and add the updated version provided by the web service.
Everything appeared to be working properly until I fired up the Instruments panel to find out that my system is leaking the objects every time it loads them from the web service (from the second time on). The core method where things appear to be messing up is this one, which is located in my HollerStore singleton class:
- (void)addHoller: (Holler *)h
{
//Take a holler, check to see if one like it already exists
int i = 0;
NSArray *theHollers = [NSArray arrayWithArray:allHollers];
for( Holler *th in theHollers )
{
if( [[th hollerId]isEqualToString:[h hollerId]] )
{
NSLog(#"Removing holler at index %i", i);
[allHollers removeObjectAtIndex:i];
}
i++;
}
[allHollers addObject:h];
}
Quick explanation: I decided to copy the allHollers NSMutableArray into theHollers because it's being updated asynchronously by NSURLConnection. If I update it directly, it results in a crash. As such, I switched to this model hoping to solve the problem, however the Instruments panel is telling me that my objects are leaking. All the counts are exactly the # of items I have in my data set.
From what I can tell removeObjectAtIndex isn't effectively removing the items. Would love to get the thoughts of anybody else out there on three things:
Is my analysis correct that something else must be retaining the individual hollers being added?
Should I be using CoreData or SQLite for storing information pulled from the web service?
Do you know how long data stored in a Singleton should be available for? Until the app is killed?
Update
I think I've found the source, however perhaps someone can provide some clarity on the proper way to do this. I've created a method called parseHoller which takes a dictionary object created through SBJSON and returns my own model (Holler). Here are the last couple lines:
Holler *h = [[[Holler alloc] initFromApiResponse:hollerId
creatorId:creatorId
creatorName:creatorName
creatorImageUrl:creatorImage
comments:comments
attendees:attendees
wishes:wishes
invitees:invites
createdAt:createdAt
text:text
title:title
when:when]autorelease];
//(some other autorelease stuff is here to clean up the internal method)
return h;
I figured that since I'm returning an autoreleased object, this should be fine. Do you see anything wrong with this?
Have you tried to do a retain count on the objects that is leaking? Maybe that could clear up when or where it is being retained.
The code should be
[putObjectHere retainCount];
and then write to an NSLog
Hope it gives you something
Peter
I have an application that downloads an xml file, parses the file, and creates core data objects while doing so. In the parse code I have a function called 'emptydatacontext' that removes all items from Core Data before creating replacements items from the xml data. This method looks like this:
-(void) emptyDataContext
{
NSFetchRequest * allCon = [[NSFetchRequest alloc] init];
[allCon setEntity:[NSEntityDescription entityForName:#"Condition" inManagedObjectContext:managedObjectContext]];
NSError * error = nil;
NSArray * conditions = [managedObjectContext executeFetchRequest:allCon error:&error];
DebugLog(#"ERROR: %#",error);
DebugLog(#"RETRIEVED: %#", conditions);
[allCon release];
for (NSManagedObject * condition in conditions) {
[managedObjectContext deleteObject:condition];
}
// Update the data model effectivly removing the objects we removed above.
//NSError *error;
if (![managedObjectContext save:&error]) {
DebugLog(#"%#", [error domain]);
}
}
The first time this runs it deletes all objects and functions as it should - creating new objects from the xml file. I created a 'update' button that starts the exact same process of retrieving the file the proceeding with the parse & build. All is well until its time to delete the core data objects. This 'deleteObject' call creates a "EXC_BAD_ACCESS" error each time. This only happens on the second time through.
Captured errors return null. If I log the 'conditions' array I get a list of NSManagedObjects on the first run. On the second this log request causes a crash exactly as the deleteObject call does.
I have a feeling it is something very simple I'm missing or not doing correctly to cause this behavior. The data works great on my tableviews - its only when trying to update I get the crashes.
I have spent days & days on this trying numerous alternative methods. Whats left of my hair is falling out. I'd be willing to ante up some cash for anyone willing to look at my code and see what I'm doing wrong. Just need to get past this hurdle.
Thanks in advance for the help!
Did you save the context after removing the objects in the for loop? Be aware that deleteObject: does not delete the object immediately, it simply schedule it for deletion when changes are committed, i.e., when you save the context.
EDIT: Your problem may be related to how you present your data to the user on your table view. Without additional code is difficult to tell exactly (are you using NSFetchedResultsController or not?), but my guess is that the interaction between deleting the data and showing them on the table is not correct. Probably, what is happening is that your table is told to visualize your data, but then, when you delete them, you are not updating correctly the table.
Wow, so after a few days of testing I went down a path that led me to tracking down Zombies & memory mgmt. This was a situation where the errors I was recieving were leftover from issues in another area. When parsing the data and placing it into Core Data I released an object that was to be autoreleased. So any subsequent calls to that item within core data (save, deletec, etc) caused a crash. Thank you all for your help and I vow to pay closer attention with my memory mgmt calls.