У меня возникла проблема с синхронизацией при загрузке ресурса из ALAssetsLibrary.

На самом деле я пытаюсь загрузить несколько изображений из фотопленки, URL-адреса которых задаются некоторым запросом к базе данных. Теперь, после получения URL-адресов из базы данных, я использую эти URL-адреса для загрузки изображений с помощью метода assetForURL из ALAssetsLibrary, и после загрузки изображения я показываю изображение в некотором представлении. Поэтому я вызываю метод внутри цикла, который выполняется каждый раз, когда набор результатов запроса возвращает запись. И до сих пор все работает нормально. Ниже приведен пример кода для демонстрации процесса:

ALAssetsLibrary* library = [ALAssetsLibrary new];
//dispatch_group_t queueGroup = dispatch_group_create();
while ([rs next]) {
    //some data load up

    //load thumbnails of available images
    [library assetForURL:url resultBlock:^(ALAsset *asset) {
        UIImage* img = [[UIImage imageWithCGImage:asset.aspectRatioThumbnail] retain];

        //dispatch_group_async(queueGroup, dispatch_get_main_queue(), ^{
            //create views and add to container view
            CGFloat left = (8.0f + dimension) * i + 8.0f;
            CGRect rect = CGRectMake(left, 8.0f, dimension, dimension);
            TileView* tileView = [[NSBundle mainBundle] loadNibNamed:@"TileView" owner:nil options:nil][0];
            [tileView setFrame:rect];
            tileView.tag = i;
            tileView.active = NO;
            [self.thumbnailContainer addSubview:tileView];

                            //display image in tileView, etc.

                            //.............
            if (img) {
                [img release];
            }
            NSLog(@"block %d: %d",i,[self.thumbnailContainer.subviews count]);
            //});
    } failureBlock:^(NSError *error) {
        NSLog(@"failed to load image");
    }];

    i++;
}

NSLog(@"outside block %d",[self.thumbnailContainer.subviews count]);
[library release];

В моем коде self.thumbnailContainer - это UIScrollView, и внутри я добавляю свои пользовательские представления для отображения миниатюрных изображений, и он работает, как ожидалось.

Настоящая дилемма возникает, когда я пытаюсь выбрать самое последнее представление, добавленное в self.thumbnailContainer. Я не могу найти способ определить, когда все асинхронные блоки методов assetForURL завершены, так что self.thumbnailContainer фактически содержит некоторые подпредставления. Поэтому, если я регистрирую количество подвидов self.thumbnailContainer сразу после завершения цикла, он показывает 0. И после этого я обнаруживаю, что все блочные коды выполняются, увеличивая количество подвидов. Это очень ожидаемое поведение, но противоречит моим требованиям. Я пробовал методы dispatch_group_ и dispatch_wait из GCD, но безуспешно.

Может ли кто-нибудь предложить обходной путь или альтернативный шаблон кодирования для преодоления ситуации. Любая помощь будет высоко ценится. Спасибо.

1
Ayan Sengupta 21 Дек 2013 в 05:06

2 ответа

Лучший ответ

Вы могли бы использовать группу отправки, о чем, вероятно, имели в виду:

- (void) loadViewsWithCompletion:(completion_t)completionHandler {
    ALAssetsLibrary* library = [ALAssetsLibrary new];
    dispatch_group_t group = dispatch_group_create();
    while ([rs next]) {
        dispatch_group_enter(group);
        [library assetForURL:url resultBlock:^(ALAsset *asset) {
            UIImage* img = [[UIImage imageWithCGImage:asset.aspectRatioThumbnail] retain];

            dispatch_async(dispatch_get_main_queue(), ^{
                //create views and add to container view
                ...

                dispatch_group_leave(group);
            });
        } failureBlock:^(NSError *error) {
            NSLog(@"failed to load image");
            dispatch_group_leave(group);
        }];

        i++;
    }
    [library release];
    if (completionHandler) {
        dispatch_group_notify(group, ^{
            completionHandler(someResult);
        });
    }
    ...  release dispatch group if not ARC
}

Однако код может иметь потенциальную проблему:

Поскольку вы загружаете изображения асинхронно, они могут загружаться все параллельно. Это может потреблять много системных ресурсов. В этом случае, который зависит от реализации метода загрузчика ресурсов assetForURL:resultBlock:failureBlock:, вам необходимо сериализовать цикл.

Примечание. Метод assetForURL:resultBlock:failureBlock: может уже гарантировать сериализацию доступа к библиотеке ресурсов. Если контекст выполнения блока завершения также является последовательной очередью, [UIImage imageWithCGImage:asset.aspectRatioThumbnail] будет выполняться последовательно. В этом случае ваш цикл просто ставит в очередь несколько задач , но обрабатывает только одно изображение за раз, и вы в безопасности.

В противном случае, если метод assetForURL:resultBlock:failureBlock: выполняется параллельно и / или блок выполняет параллельную очередь, изображения могут загружаться и обрабатываться параллельно. Это может быть плохим, если эти изображения большие.

4
CouchDeveloper 21 Дек 2013 в 09:00

Два способа решить эту проблему. Они есть

1. NSLock . В частности, NSConditionLock . Обычно вы создаете блокировку с условием «отложенные задачи». Затем в блоках завершения и ошибки в assetForURL вы разблокируете его с условием «все выполнено». После того, как вы вызовете assetForURL, просто вызовите lockWhenCondition, используя свой «полностью завершенный» идентификатор, и все готово (он будет ждать, пока блоки не установят это условие)!

Проверьте это для получения дополнительных сведений

2. Вручную отслеживайте количество в resultBlock и failureBlock

Примечание.

Я упомянул несколько важных моментов, касающихся производительности производительности библиотек ALAsset в своем ответе.

1
Community 23 Май 2017 в 11:44