4

From what I've been reading about the largely undocumented NSAsynchronousFetchRequest, it is supposed to be cancelable. In Apple's video "What's New in Core Data" from WWDC 2014, there is an example of it being done (right around 17:40). But nowhere have I found how this is supposed to be done.

I've tried setting it up to cancel a fetch when a new fetch comes in, but I have been, seemingly, unsuccessful in getting this to work. The reason I say "seemingly" is because when I debug the code, it hits the cancel method of NSAsyncronousFetchResult's NSProgress property (and the property is not nil). However, after several previous fetches have been "cancelled" the app freezes for approximately the amount of time it would have taken to perform all the fetches. So, it doesn't seem like the fetches are being canceled. Here is what I am trying to cancel the fetch:

if (self.asyncFetchResult) {
    [self.asyncFetchResult.progress cancel];
}

NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"OfflineFeature"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"layers.layerName in %@ AND xMax >= %lf AND xMin <= %lf AND yMax >= %lf AND yMin <=%lf", allLayerNames, bufferedEnvelope.xmin,bufferedEnvelope.xmax,bufferedEnvelope.ymin,bufferedEnvelope.ymax];

NSAsynchronousFetchRequest* asyncFetchRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest:fetchRequest completionBlock:^(NSAsynchronousFetchResult* result) {
    if (![result.progress isCancelled]) {
        allFeatures = result.finalResult;
        dispatch_async(dispatch_get_main_queue(), ^{
            //Bunch of code to use the results
        });
    }
}];

MVAppDelegate* appDelegate = (MVAppDelegate*)[[UIApplication sharedApplication] delegate];
__weak typeof(self) weakSelf = self;

[appDelegate.managedObjectContext performBlock:^{
    NSProgress* progress = [NSProgress progressWithTotalUnitCount:1];
    [progress becomeCurrentWithPendingUnitCount:1];
    NSError* error;
    weakSelf.asyncFetchResult = [appDelegate.managedObjectContext executeRequest:asyncFetchRequest error:&error];
    if (error) {
        NSLog(@"Error performing asynchronous fetch request.\n%@", error);
    }
    [progress resignCurrent];
}];

I would appreciate any thoughts on what I'm doing wrong or if there's something else I could try that may be more appropriate. Thanks in advance.

dudeman
  • 1,106
  • 8
  • 19
  • I don't see anything horribly wrong here. You might want to check if the progress is cancelled on the main thread (where you have `//Bunch of code to use the results`). But even if the requests aren't getting cancelled, your app shouldn't be freezing. Are you sure it's the `NSAsynchronousFetchRequest` that's blocking and not some other work? Have you tried taking a stackshot while it's frozen? – Aaron Brager Dec 29 '15 at 18:46
  • @AaronBrager: I'm pretty sure it's not the `Bunch of code to use the results`, because of the amount of time I know that takes compared to the amount of time it's frozen. Also, the disk utilization when it's frozen is extremely high (25+ MB/s). I haven't taken a stackshot, though. Would I do that just by pausing the app while it's frozen? – dudeman Dec 29 '15 at 18:49
  • @AaronBrager: I think I figured out the issue (thanks to your suggestion of looking at the current stack). It looks like there is another fetch being performed on the same moc that is blocking the main thread. My initial guess is that all the previous fetches I have started completed before this fetch starts. – dudeman Dec 29 '15 at 19:04

0 Answers0