I tried to save data and merge with CoreData and multi-thread for iPhone app.
But I can't get managed objects in the main thread after merging.
I wrote code just like this:
[managedObjectContext performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
[self performSelectorOnMainThread:#selector(didMerged:) withObject:objectIds waitUntilDone:YES];
So I tried to pass objectIds to get NSManagedObject instances in the main thread which were generated in another thread. At first I tried "objectWithId" method but it generated fault objects. Then I tried "existingObjectWithID" method but it generated objects partly and others were nil with following Error:
[Error] Error Domain=NSCocoaErrorDomain Code=133000 "Operation could not be completed. (Cocoa error 133000.)"
What is wrong? Is there any way how to retrieve all objects by objectIds after merging in another thread?
Thank you.
There are two types of object ids. Before you save a NSManagedObject it has temporary object id. After saving, it will get its final id. So you may use the wrong id...
Read Managed Object IDs and URIs here: https://developer.apple.com/documentation/coredata/nsmanagedobjectid
It seems your context merge failed.
developer documentation on error 133000
NSManagedObjectReferentialIntegrityError = 133000
NSManagedObjectReferentialIntegrityError
Error code to denote an attempt to fire a fault pointing to an object that does not exist.
The store is accessible, but the object corresponding to the fault cannot be found.
Available in Mac OS X v10.4 and later.
Declared in CoreDataErrors.h.
First, you need to unroll your errors. Change the output to:
NSLog(#"Error: %#\n%#", [error localizedDescription], [error userInfo]);
That will give you a lot more information.
Second, if you are working with a single context in multiple threads you are doing it wrong. You need to review the documentation on Core Data and threading. The basic rule is: One Context Per Thread; Period. If you need to manage data across multiple threads look into watching the save notifications from the background threads on the main thread. I would suggest reviewing my articles on the Mac Developer Network for examples of this.
Related
After adding a new NSManagedObject to my Core Data store I tried calling:
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
and got the following exception (weirdly I had no error and the result was also positive!)
2013-03-15 18:32:09.753 Nick copy[28782:3407] CoreData: Ubiquity: An exception occured during a log file export: NSInternalInconsistencyException save notification contents: NSConcreteNotification 0x3891b0 {name = _NSSQLCoreTransactionStateChangeNotification; object = (URL: file://localhost/var/mobile/Applications/FCAF7FC6-7DC8-4E0B-A114-38778255CA90/Documents/MyApp.sqlite); userInfo = {
"_NSSQLCoreActiveSaveRequest" = "";
"_NSSQLCoreTransactionType" = 2;
"_NSSQLCoreTransientSequenceNumber" = 1;
}}
I can catch all exceptions from the "save" method and the App runs fine. Just wondering if this is really save to do, because it feels totally unsafe.
EDIT: Another exception when trying to delete an Object:
Catched Exception: Failed to process pending changes before save. The context is still dirty after 100 attempts. Typically this recursive dirtying is caused by a bad validation method, -willSave, or notification handler.
Is it safe? Probably not. The error shows that the underlying ubiquity system failed to create a SQL log file. That probably means that it failed to create the transaction log that iCloud would use to sync changes. Catching it and continuing means that your data probably saved locally, depending on the details of the framework code. But it almost certainly means that the changes will not be synced by iCloud. Worse, you could well be in a situation where future saves will also fail for the same reason.
I'm not completely sure about the second exception but it's very likely to be a side-effect of the first one.
If you're attempting to use Core Data's built-in iCloud support on iOS5, this is just the beginning of the weird, inexplicable errors. I've done a fair amount of iCloud/Core Data work and I really can't recommend using it with iOS 5. Even iOS 6 is dicey at best, but problems are less likely than on iOS 5.
Unfortunately I can't find the thread anymore, but it told me I had to make sure to always use NSManagedObject classes only in the thread/dispatch_queue in which they are created.
The problem is, if you do access it from a different queue, it might work or crash after a random interval.
I made sure I call NSManagedObject from a dedicated dispatch_queue only and have not logged any weird exceptions since then.
Currently I'm using Restkit to control all my (Core-) data in my app. I'm using it to keep in sync with the server using RKManagedObjectMapping and I use [myMyNSManagedObject createEntitity] together with [[RKObjectManager §sharedManager].objectStore save] to manually edit items within Grand Central Dispatch.
Is there any recommendation to do this in this or an other way? Because sometimes the app freezes in a deadlock executing this code of Restkit
+ (NSArray*)objectsWithFetchRequest:(NSFetchRequest*)fetchRequest {
NSError* error = nil;
NSArray* objects = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
if (objects == nil) {
RKLogError(#"Error: %#", [error localizedDescription]);
}
return objects;
}
with that
- (NSError*)save {
NSManagedObjectContext* moc = [self managedObjectContext];
NSError *error;
#try {
if (![moc save:&error]) {
if (self.delegate != nil && [self.delegate respondsToSelector:#selector(managedObjectStore:didFailToSaveContext:error:exception:)]) {
…
in parallel. Before I switched to Restkit I put a "context performBlockAndWait" around each entity-editing code and was on the safe side with no deadlocks. I have no other NSManagedObjectContext or something created by myself, all comes from Restkit.
In my case, the problem was that I was passing NSManagedObjects across thread boundaries, and using them on threads other than the ones on which they were fetched from their respective NSManagedObjectContext. It was really subtle in my case, as I knew that I wasn't supposed to do this, but did it accidentally anyways. Instead of passing the managed objects, I started passing the NSManagedObjectIDs (and then fetching from the local thread's MOC), and haven't encountered a deadlock since. I'd recommend you do a deep scan of your code to make sure you are only using managed objects in the threads that spawned them.
We've encountered this exact problem in our app. Basically, CoreData nested contexts are very buggy in iOS5, and thus don't work as advertised. This has many manifestations, but one of them is the problem described above, deadlocking a fetch request vs. a background operation. This is well documented, instructive quote:
NSFetchedResultsController deadlocks
You never want your application to deadlock. With NSFetchedResultsController and nested contexts, it’s pretty easy to do. Using the same UIManagedDocument setup described above, executing fetch requests in the private queue context while using NSFetchedResultsController with the main queue context will likely deadlock. If you start both at about the same time it happens with almost 100% consistency. NSFetchedResultsController is probably acquiring a lock that it shouldn’t be. This has been reported as fixed for an upcoming release of iOS.
This SO answer has a possible fix that keeps nested contexts. I've seen others like it, basically liberally applying -performAndWait: calls. We haven't tried that yet (iOS5 has single digit percentages in our user base). Otherwise, the only other "fix" we know right now is abandoning nested contexts for iOS5 (cf. this SO answer).
That CoreData continues to be fundamentally broken for multithreading (in iOS 5) is inexcusable on Apple's part. You can make a good case now that trusting Apple on CoreData (multithreading, iCloud) seems to be opening a bag of pain.
I got the following error when deleting an object from my app. Any ideas on how to fix it?
I'm still trying to recreate the process to find out exactly where it is crashing.
Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. CoreData could not fulfill a fault for '0x5adc8b0 <x-coredata://2B90C6EC-E046-4508-A1B1-6C4B19BF830D/Session/p1>' with userInfo {
NSAffectedObjectsErrorKey = (
"<Session: 0x6f7c830> (entity: Session; id: 0x5adc8b0 <x-coredata://2B90C6EC-E046-4508-A1B1-6C4B19BF830D/Session/p1> ; data: <fault>)"
);
*** Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x5adc8b0 <x-coredata://2B90C6EC-E046-4508-A1B1-6C4B19BF830D/Session/p1>''
A) If you are subscribing to save notifications, Core Data is pointing to a failure there.
B) The "could not fulfill a fault" message is often related to deleting an object, after which some other part of your app tries to access ANY property on it. Yes, even ones where faults were previously fulfilled. The whole object is unusable after a delete.
This occurred in my code when I passed a managed object down to a custom NSOperation class. My NSOperation would load other objects through its to-many relationship, process them, then delete those objects. If it tried to do a search on more objects in the to-many relationship, it would sometimes crash with the "CoreData could not fulfill a fault" exception. Then I remembered reading about "thread confinement" in the Core Data Programming Guide so I changed my code to pass a key down to my NSOperation class. The NSOperation class would then do its own search in its own context to find its own instance of the core data class. I stopped getting the exception after that. (I still have to use save notifications.)
I have a application that combines threading and CoreData.
I am using one global NSPersistentStoreCoordinator and a main NSManagedObjectContextModel.
I have a process where I have to download 9 files simultaneously, so I created an object to handle the download (each individual download has its own object) and save it to the persistentStoreCoordinator.
In the [NSURLConnection connectionDidFinishLoading:] method, I created a new NSManagedObject and attempt to save the data (which will also merge it with the main managedObjectContext).
I think that it is failing due to multiple process trying to save to the persistentStoreCoordinator at the same time as the downloads are finishing around the same time.
What is the easiest way to eliminate this error and still download the files independently?
The NSManagedObjectContext instances know how to lock the NSPersistentStoreCoordinator. Since you are already using one NSManagedObjectContext per thread that is most likely not the issue.
It would help to know what the error is that you are getting. Unroll the NSError and look at its -userInfo. If the userInfo dictionary contains the key NSDetailedErrors. The value associated with this key will be an array that you can loop over and look at all the errors inside. That will help to determine what is going on.
It is quite possible that the error can be something as simple as validation or a missing required value and has nothing to do with the actual threading.
I have a CoreData-driven navigation app and I'm trying to figure out why It's crashing.
I've got a hierarchy which is 3 view Controllers deep, all related by coredata relatioships, like this.
TableViewA =relationship= TableViewB =relationship= TableViewC
I'm honestly a novice at core data and I think my problem lies in the fetched results controller. I have one in TableViewA and another in TableViewB, and no matter how deep I go, the console always cites TableViewB's fetched results controller methods after a crash. Is this the problem?
What's happening specifically is if I launch my app and drill down into the hierarchy of one record, let's call it Record1, I can delete sub records to my hearts content. Gone! no problem!
But the second I go back to TableViewA and drill down into a different record, let's call that one Record2, and try to delete it's subrecords my app crashes, with the console citing this code from TableViewB as the problem.
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[self.tableView beginUpdates];
}
When I go into the debugger, the specific method it always has a problem with is:
if (![x.managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
Just a confirmation of my idiocy with CoreData is all I'm looking for I think.
Oh and how many ManagedObjectContexts should I have in an app of this type. I've been told I should have separate ones for adding content, which then should re-integrate into the main one. Is this true?
Thanks!
I feel like this is a problem where you delete subrecords for Record1, then some inconsistency occurs when you go to delete the subrecords for Record2 - the original managed object context doesn't get saved, or gets into a conflict, or some such. I'd definitely check into your Core Data object management before your table view code.
As for having multiple managed object contexts, usually you only have an additional context when you're adding entirely new records. The typical pattern is to add new records into a secondary context, then merge that context into your app's primary context once the new record is added and saved. For modification or deletion, just use the original context.
I could be more definite about the problem you're having if you post the logging output of the NSLog statement in your last code snippet. The error's domain, code, and userInfo attributes will all be tremendously helpful here.