【问题标题】:dispatch_group_t issue, dispatch_group_notify is calling back before leaving the groupdispatch_group_t 问题,dispatch_group_notify 是在离开组之前回调
【发布时间】:2014-05-10 14:10:06
【问题描述】:

我在下面有以下代码 sn-p,它在后台使用 PFQueues 从 Parse 获取数据并返回数据和状态。此结构基于等待 dispatch_group_t 通知它已完成所有输入的组。不幸的是dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ 在完成块调用 dispatch_group_leave 之前调用。在任何完成块上调用 dispatch_group_leave() 时,都会抛出 EXC_BAD_INSTRUCTION。我在下面附上了说明错误的图片。有谁知道我做错了什么,或者 Parse 是否有一些烦恼阻止我使用这种方法?

    - (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);
        }
    }];

}

上面列出的下载过程是从 AppDelegate 调用的

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

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

    }];

    return YES;
}

堆栈跟踪:

【问题讨论】:

  • 可能与错误无关,但是为什么要使用外部 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{...}); ? IMO 这没有任何意义。您可以直接删除它,然后直接调用内联块中的代码。
  • 同意@couchdeveloper。这看起来不错,除了将整个东西包装在 dispatch_async 中。
  • 这是我尚未删除的旧设置中的剩余代码,我将编辑上面的内容以删除它。我知道这是一个多余的部分。那么在这种情况下,为什么我会收到我遇到的错误呢?
  • 您是否更改了代码,或者只是问题?这看起来像是一个变量范围问题,但如果不知道您实际运行的是什么代码,就很难知道。
  • 我刚刚删除了 dispatch_async 包装器。这真的没什么区别。最后还是同样的错误。

标签: objective-c parse-platform objective-c-blocks grand-central-dispatch


【解决方案1】:

您看到的错误表明您的程序调用dispatch_group_leave 的次数过多。重现是微不足道的。我用这个程序复制了它:

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

因此,我推断您的 fetchDataWithCompletion: 方法不止一次调用其完成块。如果您不知道原因,请编辑您的问题以包含该方法的源代码(以及任何相关的方法或声明)。

【讨论】:

  • 我已经编辑了我的问题以包含上面提到的 fetchDataWithCompletion 方法。我知道与此相关的错误,但是,我不知道这是如何发生的,因为我不保留完成块,并且所有内容都应该只称为一个。
  • 我不熟悉 Parse API。你确定-[PFQuery findObjectsInBackgroundWithBlock:] 只调用一次完成块吗?在完成块中放入NSLog,看看它被调用了多少次。
  • 我已验证它仅通过日志记录调用一次。唯一一次有多个回调包括下载 PFFile 并获取下载状态。可以在此处找到文档:parse.com/docs/ios_guide#top/iOS。文档或示例中没有任何其他说明,我现有的测试通过了一个回调的场景。
  • 我已经在上面添加了堆栈跟踪。
  • 我在您的堆栈跟踪中看到fetchArtworkPhotosWithCompletion:,但在您的代码中没有看到它。为什么它在堆栈跟踪中?
【解决方案2】:

我来晚了,但很明显您的问题来自kPFCachePolicyCacheThenNetwork。 Parse 将调用完成块两次,一次是缓存数据(甚至是第一次),一次是下载数据......所以你的 dispatch_group_leave 将被调用两倍于你的 dispatch_group_enter

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多