I'd like to delete a core data object by fetched the object first, so
in FetchObject.m
- (void) actionDelete {
AModel *aModel = [[aModel alloc] init];
AObj *aObj = [aModel readDataWithAttributeName:#"keyword" attributeValue:#"value"];
[aModel deleteObject:aObj];
}
aObj did fetch and obtain.
in AModel.m
- (void)deleteObject:(AObj *)aObj
{
[appDelegate.managedObjectContext delete:aObj];
NSError *error;
if (![appDelegate.managedObjectContext save:&error]) {
NSLog(#"Error: %#", [error description]);
}
}
But, when I test it, here came out an error
-[NSManagedObjectContext delete:]: unrecognized selector sent to instance 0xa43ece0
After searching the solution a bit, seems like the target has been release before deleteObject.
Is there any way to solve the problem?
The following code is causing the issue:
[appDelegate.managedObjectContext delete:aObj];
Replace it with:
[appDelegate.managedObjectContext deleteObject:aObj];
NSManagedObjectContext doesn't have a delete method, it only has a deleteObject method.
- (void)deleteObject:(NSManagedObject *)object
Parameters
object
A managed object.
Discussion
When changes are committed, object will be removed from the uniquing
tables. If object has not yet been saved to a persistent store, it is
simply removed from the receiver.
Related
I am using RestKit with Coredata and fetching data from the server and displaying.
Now I am doing a post from the client and this object gets updated as part of the response that comes back from the server. This is where the problems starts.
I looked for the correct way to implement this and came across 2 main points.
MOCs should not be shared across threads
An object created in the MOC is not available in another thread without saving.
But i think since the record gets updated from server response, its no longer finding the orig object. I just dont knw what the right fix is.
Here is my code
1. Create local entity
NSEntityDescription *itemEntity = [NSEntityDescription entityForName:ENTITY_ITEM inManagedObjectContext:self.managedObjectContext];
Item *item = [[Item alloc]initWithEntity:itemEntity insertIntoManagedObjectContext:self.managedObjectContext];
// Set params on item here
// Then save it
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
DBGLog(#"Tried to save the new item but failed - error %#, %#", error, [error userInfo]);
}
// Then I make the RestKit call to post the item
// The server updates the item Id
[SharedManager postItem:item success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
// successful case
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
// failure case
}];
It looks like when its trying to make the response it doesnt find the object.
And i get this exception -
*** Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x156c87b0 <x-coredata://A42ABF18-01B6-4D78-B81B-62D8B604EB52/Item/p6>''
*** First throw call stack:
(0x2f853f0b 0x39feace7 0x2f5b7fd1 0x2f61a655 0x2f6246a7 0x2f6326e5 0x2f632a95 0x2f63356f 0x3a4d3d3f 0x3a4d86c3 0x2f628e7b 0x2f633271 0x2f5c7f49 0x1c67fb 0x2f62b9cd 0x3a4d9b3b 0x3a4d3d3f 0x3a4d66c3 0x2f81e681 0x2f81cf4d 0x2f787769 0x2f78754b 0x346f46d3 0x320e6891 0x72561 0x3a4e8ab7)
libc++abi.dylib: terminating with uncaught exception of type _NSCoreDataException
If I dont do a "save" then I see Cocoa Error 133000 on 4S devices. So there is definitely something I am messing up.
Appreciate any insights!
Your comment is along the correct lines, but not the correct solution. The problem is that you only save the main thread context and the change doesn't get pushed up to the parent (the persistent context). So, instead of calling if (![self.managedObjectContext save:&error]) { you should be calling if (![self.managedObjectContext saveToPersistentStore:&error]) {
I'm setting up a very simple NSIncrementalStore example using AFIncrementalStore.
The idea is to setup a NSManagedObjectContext in the AppDelegate (using the ordinary template provided by Apple, with changes for my IncrementalStore), do a fetch with no predicate or sort descriptor and NSLog the one fetched entity object.
Everything works great until I ask for any entity attribute. It crashes with the following message:
2013-07-22 16:34:46.544 AgendaWithAFIncrementalStore[82315:c07] -[_NSObjectID_id_0 eventoId]: unrecognized selector sent to instance 0x838b060
2013-07-22 16:34:46.545 AgendaWithAFIncrementalStore[82315:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_NSObjectID_id_0 eventoId]: unrecognized selector sent to instance 0x838b060'
My xcdatamodeld is correctly setted up. The NSManagedObject class is generated and imported on the delegate. When I do a breakpoint before the NSLog I can see the fetched objects IDs. The webservice is giving me back the correct data.
My AppDelegate code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
[self.window makeKeyAndVisible];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(remoteFetchHappened:) name:AFIncrementalStoreContextDidFetchRemoteValues object:self.managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:#"Agenda" inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = entityDescription;
fetchRequest.predicate = nil;
NSError *error;
[self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
return YES;
}
// Handle the notification posted when the webservice returns objects
- (void)remoteFetchHappened:(NSNotification *)aNotification
{
NSArray *fetchResult = [[aNotification userInfo] objectForKey:#"AFIncrementalStoreFetchedObjectIDs"];
Agenda *agenda = (Agenda *)[fetchResult lastObject];
// THIS IS WHERE IT BREAKS...
NSLog(#"Agenda: %#", agenda.eventoId);
}
Any ideas on how to make this piece of code return the attribute I'm asking for?
AFNetworking is giving you managed object IDs, that is, instances of NSManagedObjectID. You can't look up managed object property values on that-- you have to get the managed object for the ID first. That's what _NSObjectID_id_0 means in the error message-- you're trying to get
eventoId on an NSManagedObjectID, and it has no idea what that is.
You get the managed object by looking it up in the managed object context. Something like
NSError *error = nil;
NSManagedObject *myObject = [context existingObjectWithID:objectID error:error];
if (myObject != nil) {
// look up attribute values on myObject
}
My app simply add some users informations (name, birthdate, thumbnail, ...) with Core Data.
I noticed that if I delete a user right after created it, my app just stop working (not a crash, xCode returns no crash log, nothing).
I'm using asynchronous nested context for saving my users informations so I guess that behavior is due to the fact that my delete statement is executing before my save statement.
But since i'm a total beginner with Core Data, i don't really know how to handle that. I don't even know if i declared nested contexts the right way.
Here's my save codes :
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
tmpContext.parentContext = self.backgroundManagedObjectContext;
BSStudent *newStudent = (BSStudent *)[NSEntityDescription insertNewObjectForEntityForName:kBSStudent inManagedObjectContext:tmpContext];
newStudent.firstname = firstname;
newStudent.lastname = lastname;
newStudent.birthdate = birthdate;
newStudent.thumbnail = thumbnail;
newStudent.createdAt = [NSDate date];
[self dismissViewControllerAnimated:YES completion:nil];
[tmpContext performBlock:^{
[tmpContext save:nil];
[self.backgroundManagedObjectContext performBlock:^{
NSError *error;
if (![self.backgroundManagedObjectContext save:&error]) {
NSLog(#"%#", [error localizedDescription]);
}
[self.managedObjectContext performBlock:^{
NSError *error;
if (![self.managedObjectContext save:&error]) {
NSLog(#"%#", [error localizedDescription]);
}
}];
}];
}];
For precision, self.managedObjectContext is a NSPrivateQueueConcurrencyType and self.backgroundManagedObjectContext is a NSMainQueueConcurrencyType. And self.backgroundManagedObject is a child of self.managedObjectContext.
Here's my delete codes :
BSStudent *student = objc_getAssociatedObject(alertView, kDeleteStudentAlertAssociatedKey);
// on supprimer l'objet et on sauvegarde le contexte
[self.managedObjectContext deleteObject:student];
NSError *error;
if(![self.managedObjectContext save:&error]) {
NSLog(#"%#", [error localizedDescription]);
}
Can someone know how to handle this situation properly ?
Your delete is probably using the BSStudent created by a different context than you are deleting with. The following code will fix that.
NSManagedObjectContext * deleteContext = student.managedObjectContext;
[deleteContext deleteObject:student];
If you really want to use the other context, refetch the student using ObjectID
NSManagedObject * studentToDelete = [self.managedObjectContext objectWithID:student.objectID];
[self.managedObjectContext deleteObject:studentToDelete];
Nested contexts tips
Your contexts are probably okay, but I see a lot of people throwing around performBlock unnecessarily. With nested contexts, the QueueConcurrencyType refers to the thread it will do Core Data operations on, not the thread it was created on. So doing an operation like save on itself inside its performBlock is unnecessary and can lead to deadlocks.
When you save a child context, the parent is automatically synced with the changes. If you want to save upwards to the next higher parent automatically, I would recommend registering the parent for NSManagedObjectContextDidSaveNotification of the child saves. You can make this easier by having your AppDelegate have a factory method for creating the child contexts.
- (NSManagedObjectContext *)createChildContext
{
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
tmpContext.parentContext = self.managedObjectContext;
//Register for NSManagedObjectContextDidSaveNotification
return tmpContext;
}
if you wrap your delete in a performBlock call it can't execute at the same time as the saving performBlock.
e.g.:
BSStudent *student = objc_getAssociatedObject(alertView, kDeleteStudentAlertAssociatedKey);
// on supprimer l'objet et on sauvegarde le contexte
[self.managedObjectContext performBlock:^{
[self.managedObjectContext deleteObject:student];
NSError *error;
if(![self.managedObjectContext save:&error]) {
NSLog(#"%#", [error localizedDescription]);
}
}];
This is the "preferred" way of dealing with contexts as it serializes access to the context and keeps all those operations on the contexts thread,
I assume you are getting the crash because the objectID is becoming invalid or changing before the save completes, near the top of the call stack you'll see something about "hash64" or such
Here's my code:
for (NSString *filePaths in self.filesAdded) {
FileAttachments *fileObject = [NSEntityDescription insertNewObjectForEntityForName:#"FileAttachments" inManagedObjectContext: context];
fileObject.fileName = filePaths;
}
NSLog(#"before context->save");
if (![context save:&error]) ...
I'm trying to find the point when a NSManagedObject is saved in a context after being inserted for the first time. It seems awakeFromInsert is called when you call [NSEntityDescription insertNewObjectForEntityForName], which is too early since none of the properties. I want to know when [context save: error] is called on those new NSManagedObjects, so I can save some more meta-information. Is there any good, clean way to do this? Something like prepareForDeletion, which is perfect for cleaning up objects.
You could subclass your NSManagedObjectContext and override the save: method. You can run your "centralized" code at that point and then call save: of the super class. The call to the super class save: method is when it's actually saved, like others have said.
NSManagedObjectContext generates a number of NSNotifications that might be what you are looking for. Check the documentation for NSManagedObjectContextDidSaveNotification and NSManagedObjectContextObjectsDidChangeNotification.
In core data I've setup a simple entity called GolferEntity that contains simply a golferObject (transformable type) and playerId (string).
- (void)addOrUpdateGolfer:(GolferObject *)feedObj
{
NSLog(#"In add or update Feed");
// get reference to local (stored) golfer item, create it if needed
Golfer *localGolfer = [self golferForId:[feedObj PlayerId]];
if (localGolfer == nil) {
localGolfer = (Golfer *)[NSEntityDescription insertNewObjectForEntityForName:#"GolferEntity" inManagedObjectContext:[self managedObjectContext]];
[localGolfer setPlayerId:[feedObj PlayerId]];
}
// set folder fields
[localGolfer setGolferObj:feedObj];
// apply update
NSError *error;
if (![managedObjectContext save:&error]) {
NSLog(#"%#", [error localizedDescription]);
// return nil;
}
NSLog(#"successfully saved user: %#", [feedObj PlayerId]);
//return localFolder;
}
The code is giving me a runtime error which reads:
* -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[GolferObject encodeWithCoder:]: unrecognized selector sent to instance 0x4b7520'
-- No where in my code have i alloc'd NSKeyedArchiver so I'm assuming this is something that done by core data? also my GolferObject does not have an encodeWithCoder method? I do not know where this is coming from?
I needed to encode before submitting to my custom object in CoreData. I needed to use the - (void)encodeWithCoder:(NSCoder *)encoder; and - (id)initWithCoder:(NSCoder *)decoder; methods.