Sometimes when I call saveToPersistent on the RestKit MainQueueManagedObjectContext I get the following error and the app crashes.
CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[UIBarButtonItem controllerWillChangeContent:]: unrecognized selector sent to instance 0x21e70300 with userInfo (null)
[UIBarButtonItem controllerWillChangeContent:]: unrecognized selector sent to instance 0x21e70300 with userInfo (null)
It seems that there are some kind of threading problem.
I save the context via a method like so:
- (BOOL)saveMainContext
{
__block BOOL contextSaved = NO;
[[[[CoreDataManager sharedInstance] objectStore] mainQueueManagedObjectContext] performBlockAndWait:^{
NSError *error = nil;
if ([[[[CoreDataManager sharedInstance] objectStore] mainQueueManagedObjectContext] saveToPersistentStore:&error]) {
contextSaved = YES;
}
}];
return contextSaved;
}
It crashes on the saveToPersistent method with EXC_BAD_ACCESS.
How can I fix this and what could be the reason for my NSFetchresultscontroller calling a method on a UIBarButtonItem?
Your saveMainContext method should only be called from the main thread and should simply be implemented as:
- (BOOL)saveMainContext
{
NSError *error = nil;
if ([[[[CoreDataManager sharedInstance] objectStore] mainQueueManagedObjectContext] saveToPersistentStore:&error]) {
contextSaved = YES;
} else {
NSLog(#"Save error: %#", error);
}
return contextSaved;
}
Calling an instance of UIBarButtonItem suggests that you have a memory management issue in that something which is observing the context save is not removing itself before it is deallocated. Check all of your observers.
Related
I thought that [moc existingObjectWithID:self.objectID error:&error] was the right way to find an existing object inside of a thread, given its objectID, but I can't get it to work, why?
Background / Code
I've got an NSManagedObject which is already saved. I can see all its properties on the main thread. I'm passing the object to my NSOperation in a custom init method and in that method, I'm saving its objectID:
- (instancetype)initWithEntity:(NSManagedObject *)object
{
self = [super init];
if (self) {
self.objectID = object.objectID;
...
}
return self;
}
Then in the main function of my operation I'm looking up my object so that I can use it.
- (void)main
{
if (self.isCancelled) { return; }
NSManagedObjectContext *moc = [NSManagedObjectContext MR_context];
NSError *error = nil;
self.object = [moc existingObjectWithID:self.objectID error:&error];
if (error) {
DDLogError(#"error finding object: %#", error);
}
...
}
MR_context is a MagicalRecord method which simply sets up a new context with concurrency type NSPrivateQueueConcurrencyType with its parent set to the root saving context. All of that looks correct to me.
Errors
When no debugger is set, I simply get a fault for the object. If I try to look up any property on it, I get nil.
With the debugger turned on and -com.apple.CoreData.ConcurrencyDebug 1 set I get some errors:
In the simulator I get EXC_BAD_INSTRUCTION (code=EXC_i386_INVOP, subcode=0x0) on the [moc existingObjectWithID:self.objectID error:&error] line.
On the device, I get the following on that same line:
CoreData`+[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__]:
-> 0x185946614 <+0>: brk #0x1
So obviously something is wrong, but the objectID isn't temporary, it is the same objectID I see when inspecting the real object on the main thread. So I'm not sure why it isn't working.
I've also tried:
self.object = [moc objectWithID:self.objectID];
But that doesn't work either. Any ideas?
Updates
2015-07-10 - Ok, if I use a block with the moc then I can properly access the object from within that block.
[moc performBlock:^{
NSError *error = nil;
NSManagedObject *o = [moc existingObjectWithID:self.objectID error:&error];
}];
How do I properly extract that object so I can have access to it in the thread that the operation is running? Doing a __block property and then setting it in the block and using it out of the block failed.
Is this an issue with the way the context is setup?
MagicalRecord's context sets up its own queue to perform operations on it. Therefore, if you want to use it to look something up, you have to do it in a block ([context performBlock:] or [context performBlockAndWait:]) so that it can perform it on its own queue and then return back to the NSOperation.
What I ended up doing was creating a couple __block properties and then pulling the needed contents from the object outside of the block.
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.
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
}
Everyone once in a while I will get crash reports for my App (via crittercism) with the following NSInvalidArgumentException crash:
+[GKAchievement reportAchievements:withCompletionHandler:]: unrecognized selector sent to class 0x3f940a84
The code looks like this:
NSMutableArray *achivementArray = [NSMutableArray arrayWithCapacity:20];
for (NSString *achievementID in achievementsToUpload) // achievementsToUpload is a NSSet
{
GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier:achievementID];
if (achievement)
{
...
[achivementArray addObject:achievement];
}
}
[GKAchievement reportAchievements:achivementArray withCompletionHandler:^(NSError *error)
{
if (error != nil)
{
NSLog(#"Error sending achievement: %#", error.localizedDescription);
}
}];
If the error is indeed inside reportAchievements:withCompletionHandler:, as far as I can see, the source of the error can come from two places: 1) achivementArray is not an NSArray or 2) error is not really an NSError.
In the case of #1, I create the array a few lines above the call and in case #2, I doubt the OS is sending back a non-NSError object.
Any ideas?
Header looks like:
+ (void)reportAchievements:(NSArray *)achievements withCompletionHandler:(void(^)(NSError *error))completionHandler __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_6_0);
So the issue may be in base sdk that you are using. This method is available starting from iOS 6.0.
Hope it will be helpful
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.