方法中的块代码无法运行

时间:2014-05-28 12:54:38

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

我有以下代码加载一组图像,并在完成加载后通过completionHandler通知我。但是,我发现某些dispatch_group_leave有时不会被调用,我的猜测是在块运行之前取消分配imageLoader。如果我在loadImageWithURL:completionHandler块中放入了imageLoader的引用,那么一切都按预期工作。

我对原因的猜测是否正确?这个问题的正确解决方法是什么?我知道ARC在大多数情况下会自动阻止复制,在这种情况下我应该进行块复制吗?

- (void)loadGroupImagesAsyncWithCompletion:(void(^)(NSError *))completionHandler {

    dispatch_group_t group = dispatch_group_create();
    int index = 0;

    for (Item *item in items) {

        char queueLabel[30] = {0};
        sprintf(queueLabel, "loader%d", index);
        dispatch_queue_t queue = dispatch_queue_create(queueLabel, NULL);

        dispatch_group_enter(group);
        dispatch_async(queue, ^{

          ImageLoader *imageLoader = [[ImageLoader alloc] init];
          [imageLoader loadImageWithURL:url completionHandler:^(UIImage *image, NSError *error) {
              if (image) {
                 item.image = image;
              }

              //NOTE: if item object is referenced in this block, 
              //then there is no missed dispatch_group_leave call.
              dispatch_group_leave(group);
          }];
        });
    }

    // Non-blocking wait
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // shouldn't take more than 5 secs to load all images
        dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)));
        dispatch_async(dispatch_get_main_queue(), ^{
            completionHandler(nil);
        });
    });
}

1 个答案:

答案 0 :(得分:0)

这是我的猜测。这只是猜测,因为您还没有发布与ImageLoader有关的代码。

如果-loadImageWithURL:completionHandler:以异步方式运行,即它使用异步调度队列本身,那么在加载完成之前它被释放可能是正确的。这是因为它的生命周期只是声明它的for块的范围。

实际上,没有理由为什么该方法需要异步执行,因为您已经在异步块中获取了它。只需一个同步方法,并在方法完成后调用dispatch_group_leave()

修改

鉴于您无法控制ImageLoader并且-loadImageWithURL:completionHandler:在没有任何帮助的情况下异步操作,您应该删除呼叫周围的dispatch_async包装器。您仍然会遇到释放ImageLoader的问题,但可以通过在创建时将每个ImageLoader放入数组中来避免这种情况。

代码看起来像这样:

- (void)loadGroupImagesAsyncWithCompletion:(void(^)(NSError *))completionHandler 
{

    NSMutableArray* loaders = [[NSMutableArray alloc] init];

    dispatch_group_t group = dispatch_group_create();
    int index = 0;

    for (Item *item in items) {

        char queueLabel[30] = {0};
        sprintf(queueLabel, "loader%d", index);
        dispatch_queue_t queue = dispatch_queue_create(queueLabel, NULL);

        dispatch_group_enter(group);
        ImageLoader *imageLoader = [[ImageLoader alloc] init];
        [imageLoader loadImageWithURL:url completionHandler:^(UIImage *image, NSError *error) {
            if (image) {
               item.image = image;
            }

              //NOTE: if item object is referenced in this block, 
              //then there is no missed dispatch_group_leave call.
              dispatch_group_leave(group);
        }];
        [loaders addObject: imageLoader];
    }

    // Non-blocking wait
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // shouldn't take more than 5 secs to load all images
        dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)));
        dispatch_async(dispatch_get_main_queue(), ^{
            completionHandler(nil);
        });
        [loaders removeAllObjects];
    });
}