CoreData asynchronous fetch causes concurrency debugger error - ios

I'm using the
-com.apple.CoreData.ConcurrencyDebug
argument on launch to debug concurrency in my CoreData app.
During app launch, I perform an asynchronous fetch on the main thread's managed object context.
// set up the async request
NSError * error = nil;
[MOC executeRequest:asyncFetch error:&error];
if (error) {
NSLog(#"Unable to execute fetch request.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
This code is called from the main thread, but executeRequest: enqueues it to another thread, which I understand to be the correct behavior.
The concurrency debugger doesn't like this, saying (I reckon) that I'm doing something wrong here. I've also tried wrapping this in [MOC performBlock:] which also works, but also causes a multithreading violation. In both cases I get this :
[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__
Am I using async fetches incorrectly, or is the concurrency debugger wrong here?
EDIT : I've also tried wrapping it in MOC performBlock which should ensure that it gets called from the main thread. In any case, the call is enqueued from the main thread, but executed elsewhere.
EDIT : here's the fetch request:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"MyEntity"];
NSPredicate * pred = [NSPredicate predicateWithFormat:#"boolProperty == YES"];
fetchRequest.predicate = pred;
NSSortDescriptor * sort = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
fetchRequest.sortDescriptors = #[sort]; fetchRequest.propertiesToFetch = #[#"prop1", #"prop2", #"prop3", #"prop4"];
NSPersistentStoreAsynchronousFetchResultCompletionBlock resultBlock = ^(NSAsynchronousFetchResult *result) {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:kFetchCompleteNotification object:result];
});
};
NSAsynchronousFetchRequest *asyncFetch = [[NSAsynchronousFetchRequest alloc]
initWithFetchRequest:fetchRequest
completionBlock:resultBlock];
Then I receive the results from the notification:
- (void)fetchCompletedNote:(NSNotification *)note {
NSAsynchronousFetchResult * result = note.object;
if (![cachedResults isEqualToArray:result.finalResult]){
cacheResults = result.finalResult;
[self.collectionView reloadData];
}
}

I think this is Apple's bug.
I filed bug report:
https://openradar.appspot.com/30692722
and added example project that reproduces issue:
https://github.com/jcavar/examples/tree/master/TestAsyncFetchCoreData
Also, if you don't want to disable flag just because of this issue you may want to swizzle __Multithreading_Violation_AllThatIsLeftToUsIsHonor__ method on NSManagedObjectContext just for this part of code.
You need to revert that after request is executed so you get violations for real issues.

The concurrency debugger is telling you that you are accessing MOC from the wrong thread/queue. You can only call -executeRequest: error: on the thread/queue that the context belongs to. If this is a NSMainQueueConcurrencyType then you need to be on the main thread. Otherwise, if it is a NSPrivateQueueConcurrencyType you need to use either -performBlock: or -performBlockAndWait: to run the execute on the correct queue.
I added a screenshot, see above. The request is enqueued from the main thread, but executed on another.
Ok, a couple of things:
Is that the line that is breaking/crashing or are you seeing the error output?
Your error handling is incorrect. You should be looking at the result of the -executeRequest: error: and if the result is nil then you are in an error state. The error variable can be populated even with a success.
I note that the code you posted in your screenshot is different than the code you posted previously. Did you add the -performBlock: or just not include it originally?

Related

Finding NSManagedObject inside of NSOperation

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.

dispatch_async never finishes executing entire block

I'm new to GCD, and what seems to be a simple use of it doesn't work for me. I have the following code:
+ (void)synchronizationTimerFired:(NSTimer *)theTimer
{
if ((synchronizationUpNeededFlag) || (synchronizationDownNeededFlag))
{
if ((!synchronizationUpInProgressDepthQuantity) && (!synchronizationDownInProgressDepthQuantity))
{
dispatch_queue_t synchronizationQueue = dispatch_queue_create("synchronizationQueue",NULL);
dispatch_async(synchronizationQueue, ^(void) {
NSLog(#"Top");
...code...
...code...
...code...
NSLog(#"Bottom");
});
}
}
// Check if there is no timer, or if it is not currently valid,
// and yet if synchronization is turned on,
// then establish a repeating timer to attend to synchronization related matters.
if ((!synchronizationTimer) || (!synchronizationTimer.isValid))
{
if (synchronizationOnFlag)
{
synchronizationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(synchronizationTimerFired:) userInfo:nil repeats:YES];
}
}
}
The log reads "Top" and nothing else. The code in the middle doesn't have an endless loop- it just never executes all the way through. I can put breakpoints in the code in the middle and there's a point where the program execution will break, and after which it won't. And there's a point right in the middle where sometimes the execution will stop at the breakpoint and other times it doesn't.
It seems to me as though the synchronizationQueue dispatch queue is being deallocated, but I can't call dispatch_retain because the compiler complains that dispatch_retain cannot be used in ARC. What am I missing?
In response to people asking about the code in-between, the program execution stops in this method call (represented by one of those lines of ...code...) at the line that says if (fetchArray.count), commented below.
+ (NSDate *)latestParseReceivedDownUpdatedAtDateForCoreDataEntityNameString:(NSString *)coreDataEntityNameString
{
NSDate *functionReturnValue = nil;
// Create fetchRequest
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:coreDataEntityNameString];
// Set sort descriptor
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"parseReceivedDownUpdatedAtDate" ascending:NO]]];
// We are only interested in one result
[fetchRequest setFetchLimit:1];
// Execute fetchRequest
NSError *fetchError = nil;
NSArray *fetchArray = [JBSAPPDELEGATE.managedObjectContext executeFetchRequest:fetchRequest error:&fetchError];
if (fetchArray == nil)
{
NSLog(#"Unresolved error %#, %#", fetchError, [fetchError userInfo]);
abort();
}
// If there are any records at all in our persistent store, we'll have exactly one.
// But that doesn't mean it won't be nil, as if that record has never come down from
// parse it will be a nil date on the managed object.
if (fetchArray.count) // PROGRAM EXECUTION STOPS EITHER HERE, OR JUST BEFORE HERE
{
NSManagedObject *managedObject = [fetchArray objectAtIndex:0];
functionReturnValue = [managedObject valueForKey:#"parseReceivedDownUpdatedAtDate"];
}
return functionReturnValue;
}
I will add that if I simply comment out the call to dispatch_async that everything executes fine. It just executes on the main thread, which I'd rather have it not do.
Is your managedObjectContext an NSManagedObjectContext? If so, did you create a managed object context on the specific thread that you are using for your dispatch queue? If you read the docs on NSManagedObjectContext, it says:
...a context assumes the default owner is the thread or queue that
allocated it—this is determined by the thread that calls its init
method. You should not, therefore, initialize a context on one thread
then pass it to a different thread. Instead, you should pass a
reference to a persistent store coordinator and have the receiving
thread/queue create a new context derived from that.
I bet you created yourself a deadlock somewhere.
Use the debugger. Check what every thread is doing. You'll probably find some thread that you think should be continuing and isn't.

Concrete/complete example of using NSPrivateQueueConcurrencyType?

I'm still coding my RSS reader and I have gotten to the point where I'd like things to go smoother by background filling my Feeds at once with the newest Posts.
The problem is that it crashes my app quite badly with messages such as:
2013-10-02 21:06:25.474 uRSS[97209:a0b] *** Terminating app due to uncaught
exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource
must return a cell from tableView:cellForRowAtIndexPath:'
(stacktrace)
I came to the conclusion that I am not running thread safe here and I then discovered this kind of CoreData snippets:
//Core Data's NSPrivateQueueConcurrencyType and sharing objects between threads
[context performBlock:^{
// fetch request code
NSArray *results = [context executeFetchRequest:request error:nil];
dispatch_async(dispatch_get_main_queue(), ^(void) {
Class *firstObject = [results objectAtIndex:0];
// do something with firstObject
});
}];
// Assume we have these two context (They need to be set up. Assume they are.)
NSManagedObjectContext *mainMOC = [[[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType] autorelease];
NSManagedObjectContext *backgroundMOC = [[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType] autorelease];
// Now this can safely be called from ANY thread:
[backgroundMOC performBlock:^{
NSArray *results = [context executeFetchRequest:request error:nil];
for (NSManagedObject *mo in results) {
NSManagedObjectID *moid = [mo objectID];
[mainMOC performBlock:^{
NSManagedObject *mainMO = [mainMOC objectWithID:moid];
// Do stuff with 'mainMO'. Be careful NOT to use 'mo'.
}];
}
}];
Now, what I would like to know is the following:
should the backgroundMOC be defined as a Class member property, or everytime the method that uses it is invoked?
what if this method is itself invoked asynchronously (the RSS parsing method create the objects on the fly)?
How may I securely notify my UITAbleView that my MOC's been updated so that it can refresh without crashing?
Does this only apply to fetches, or also to objects insertions, deletions, etc?
Where could I find a working example of this concept successfully applied?
1) backgroundMOC should be defined in the scope, where you use it. Say, if you use context inside of SomeClass, it's good to define it as property of SomeClass. However, usually many classes share same context (for example, it's quite OK to share mainMOC between all your viewControllers) so i suggest to define mainMOC and backgroundMOC in your AppDelegate or some other singleton.
2) It's OK. However, it's bad idea to create contexts every time — see 1 and initialize them once in singleton.
3) Take a look at NSFetchedResultsController. It's exactly what you need to setup your tableView and track CoreData changes.
4) Yes
5) Cannot really point you to working example. Find something out on developer.apple.com =)
Also remarks:
1) Your class cannot be named Class
2) Use existingObjectWithID:error:, not objectWithID: — check this answer, it was really annoying issue in my experience
3) Read about NSManagedObjectContext concurrency patterns

EXC_??? while executing executeFetchRequest

I am trying to add external data into SQLite / update existing data using Core Data.
Basically, I am given a JSON from external web service and I am using following piece of code to find out whether I should add new or update existing object in DB.
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
[fetch setEntity:[NSEntityDescription entityForName:#"name" inManagedObjectContext:context]];
[fetch setPredicate:[NSPredicate predicateWithFormat:#"id = %#", [data valueForKey:#"key"]]];
NSArray *results = [context executeFetchRequest:fetch error:nil];
if (results.count == 1)
{
// update existing
}
else
{
// add new
}
The problem is: sometimes this code leads to an exception:
Thread 1: EXC_??? (11) (code=0, subcode=0x0)
The exception is raised in NSManagedObjectContext executeFetchRequest:error:
If I continue execution of my app everything seems ok.
Should I worry about this exception?
I mean it's kind of annoying to have it but more important to know what is the cause and what are consequences of this exception?
Some additional detail (just in case it's relevant):
Code above gets executed multiple times in a loop (about 250 thousand times).
Code runs on main thread (yeah, I know, but it's kind of prototype).
Context was created on main thread.
External data is created in a background thread
[EDIT] Some more detail:
executeFetchRequest:error returns initialized array even when the exception was raised.
there is no error set when I provide error parameter to executeFetchRequest:error
That's not safe.
You should check the return of the method to make sure you was handed an array back
NSArray *results = [context executeFetchRequest:fetch error:nil];
if (!results) {
// An error occurred you should probably use the out error
}
Also CoreData seems to throw exceptions internally but handles them, so if you have an exception breakpoint set it will most likely get caught at random points from the CoreData stack - I'm saying this from past experience not sure if it's documented anywhere but it is mentioned in this video Debugging Tips - Mike Hay

Core Data & GCD: Passing the correct managed object context to custom NSManagedObjects

I get runtime errors which seem to result from my incorrect implementation of GCD in combination with my custom NSManagedObjects.
Nested in a GCD call, I am using custom NSManagedObjects which (seem to) have their own managed object contexts (= self.managedObjectContext).
I am creating the managed object context in the app delegate by using the managed object context provided by UIManagedDocument: self.managedDocument.managedObjectContext.
I don't understand how to pass the correct managed object context down to my custom NSManagedObjects. How would I need to change my code to use the correct managed object context?
This is my main method (inside a view controller):
dispatch_queue_t queue;
queue = dispatch_queue_create("queue", NULL);
dispatch_async(queue, ^{
// ...
NSDecimalNumber *value = [reportedPeriod
valueForCoa:figure.code
convertedTo:self.currencySymbol];
// ...});
}
In this main method I do not have any reference to a managed object context, I do just call valueForCoa:convertedTo: (which is coded as follows):
- (NSDecimalNumber*)valueForCoa:(NSString*)coaStr
convertedTo:(NSString*)targetCurrencyStr {
// ...
CoaMap *coa = [CoaMap coaItemForString:coaStr
inManagedObjectContext:self.managedObjectContext];
// ...
}
valueForCoa is a method in my custom subclassed NSManagedObject ReportedPeriod and uses its (default) managed object context self.managedObjectContext.
The app then usually crashes in the custom subclassed NSManagedObject CoaMap in the following method when it executes the fetch request:
+ (CoaMap*)coaItemForString:(NSString*)coaStr
inManagedObjectContext:(NSManagedObjectContext*)context {
NSFetchRequest *request = [NSFetchRequest
fetchRequestWithEntityName:NSStringFromClass([self class])];
NSPredicate *predicate =
[NSPredicate predicateWithFormat:#"coa == %#",coaStr];
request.predicate = predicate;
// ** The runtime error occurs in the following line **
NSArray *results = [context executeFetchRequest:request error:nil];
// ...
}
The error message is: Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x9a8a4a0> was mutated while being enumerated.
Could you please help me with this issue and give me some suggestions on how to improve my code to pass the correct managed object contexts (or on how to make sure that the correct context is used in all methods)?
Thank you very much!
That error generally relates to using a managed object incorrectly context across different threads or queues. You created the MOC on the main queue, but you're using it on a background queue without considering that fact. It's not wrong to use the MOC on a background queue, but you need to be aware of that and take preparations.
You didn't say how you're creating the MOC. I suggest that you should be doing this:
NSManagedObjectContext *context = [[NSManagedObjectContext alloc]
initWithConcurrencyType: NSMainQueueConcurrencyType];
With main queue concurrency you can just use it normally on the main thread. When you're in your dispatch queue though, do this:
[context performBlockAndWait:^{
NSFetchRequest *request = [NSFetchRequest
fetchRequestWithEntityName:NSStringFromClass([self class])];
NSPredicate *predicate =
[NSPredicate predicateWithFormat:#"coa == %#",coaStr];
request.predicate = predicate;
NSArray *results = [context executeFetchRequest:request error:nil];
// ...
}];
This will ensure that the MOC's work occurs on the main thread even though you're on a background queue. (Technically what it actually means is that the MOC's work in the background will be correctly synchronized with work it does on the main queue, but the result is the same: this is the safe way to do this).
A similar approach would be to use NSPrivateQueueConcurrencyType instead. If you do that, you'd use performBlock or performBlockAndWait everywhere for the MOC, not just on background threads.
First,
"how to pass the correct managed object context down to my custom NSManagedObjects."
We create NSManagedObject with NSManagedObjectContext. not the other way around. So, when you have a NSManagedObject, you can access the NSManagedObjectContext by asking its property: – managedObjectContext as listed in Apple Document
Second,
When working with CoreData, multi-threading could be a little tricky. especially for the beginner. The are all kind of details that you need to take care of.
I highly recommend you checkout the Parent-Child NSManagedContext. then, using MagicRecord.
By using MagicRecord, you can simply Grand Central Dispatch with a Block like this:
[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext){
// here use the `localContext` as your NSManagedContext and do things in the background.
// it will take care of all the rest.
}];
If you need to pass NSManagedObject into this block, remember to only pass the NSManagedObjectID instead of the proprety.
this is a example.
NSManagedObjectID *coaMapID = CoaMap.objectID;
[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext){
coaMap *aCoaMap = (coaMap *)[localContext existingObjectWithID:coaMapID error:&error];
// then use aCoaMap normally.
}];
Hope this helped.

Resources