Question

I have the following snippet of code below that fetches data from Parse using PFQueues in the background and returns data and a status. This structure is based off of waiting for the dispatch_group_t to notify that's it's completed all entered groups. Unfortunately dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ is called before the completion blocks call dispatch_group_leave. By the time the dispatch_group_leave() is called on any of the completion blocks, an EXC_BAD_INSTRUCTION is thrown. I've attached an image below for the instruction error. Does anyone know if I'm doing something wrong or if Parse has some annoyances that prevent me from using this method?

    - (void)downloadAndCacheObjectsWithCompletion:(void (^)(NSError *))callback
{

    __block NSError *downloadError1;
    __block NSError *downloadError2;
    __block NSError *downloadError3;
    __block NSError *downloadError4;

    NSLog(@"%@", NSStringFromSelector(_cmd));

        dispatch_group_t downloadGroup = dispatch_group_create();

        dispatch_group_enter(downloadGroup);
        [self fetchDataWithCompletion:^(NSArray *artwork, NSError *error) {
            downloadError1 = error;
            dispatch_group_leave(downloadGroup);
        }];

        dispatch_group_enter(downloadGroup);
        [self fetchDataWithCompletion:^(NSArray *artworkPhotos, NSError *error) {
            downloadError2 = error;
            dispatch_group_leave(downloadGroup);
        }];

        dispatch_group_enter(downloadGroup);
        [self fetchDataWithCompletion:^(NSArray *artists, NSError *error) {
            downloadError3 = error;
            dispatch_group_leave(downloadGroup);
        }];

        dispatch_group_enter(downloadGroup);
        [self fetchDataWithCompletion:^(NSArray *badges, NSError *error) {
            downloadError4 = error;
            dispatch_group_leave(downloadGroup);
        }];


        dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
            NSError *returnError;
            if (downloadError1 || downloadError2 || downloadError3 || downloadError4) {
                returnError = [[NSError alloc] initWithDomain:@"ParseFactory" code:-1 userInfo:@{NSLocalizedDescriptionKey: @"There was an error retrieving the content"}];
            }
            if (callback) {
              callback(returnError);
            }
        });

}



 - (void)fetchDataWithCompletion:(void(^)(NSArray *data, NSError *error))callback
{
    NSLog(@"Fetching Data");
    if ([self.cachedData objectForKey:kDataClassName]) {
        if (callback) {
            callback([self.cachedData objectForKey:kDataClassName], nil);
        }
        return;
    }
    PFQuery *dataQueue = [PFQuery queryWithClassName:kDataClassName];
    dataQueue.cachePolicy = kPFCachePolicyCacheThenNetwork;
    [dataQueue findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {

        if (!error) {
            [self.cachedData setObject:objects forKey:kDataClassName];
        } else {
           NSLog(@"Fetching Data Error: %@", error);
        }
        if (callback) {
            callback(objects, error);
        }
    }];

}

The download process listed above is called from AppDelegate as such

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//Register PFObject subclasses
    [Data registerSubclass];

[Parse setApplicationId:@"appkey" clientKey:@"clientkey"];
    [[ParseFactory sharedInstance] downloadAndCacheObjectsWithCompletion:^(NSError *error) {

    }];

    return YES;
}

enter image description here

Stack trace: enter image description here enter image description here

Was it helpful?

Solution

The error you're seeing indicates that your program calls dispatch_group_leave too many times. It's trivial to reproduce. I reproduced it with this program:

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        dispatch_group_t group = dispatch_group_create();
        dispatch_group_leave(group);
    }
    return 0;
}

Therefore I deduce that your fetchDataWithCompletion: method calls its completion block more than once. If you can't figure out why, edit your question to include the source code of that method (and any related methods or declarations).

OTHER TIPS

I come in late, but it seems clear your issue comes from the kPFCachePolicyCacheThenNetwork. Parse will call the completion block twice, one with cached data (even the 1st time), one with downloaded data... so your dispatch_group_leave will be called twice as much as your dispatch_group_enter.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top