Почувствуйте отставание при прокрутке и одновременно загрузите полноэкранное изображение из ресурса в фоновом потоке

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

[self.assetsLibrary assetForURL:[NSURL URLWithString:[[self.assets objectAtIndex:index] objectForKey:@"asset_url"]]
    resultBlock:^(ALAsset *asset) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            CGImageRef cgImage = asset.defaultRepresentation.fullScreenImage;
            UIImage *image = [UIImage imageWithCGImage:cgImage];
            dispatch_async(dispatch_get_main_queue(), ^{
                imageView.image = image;
            });
        });
                    }
failureBlock:^(NSError *error) {
    NSLog(@"error");
}];

Я знаю, что загружать полноэкранное изображение дорого, поэтому я помещаю его в фоновый поток, но при прокрутке оно все еще запаздывает. И все равно лаг даже меняю это так:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                            CGImageRef cgImage = asset.defaultRepresentation.fullScreenImage;
                            UIImage *image = [UIImage imageWithCGImage:cgImage];
                            imageView.image = image;
                            dispatch_async(dispatch_get_main_queue(), ^{
                            });
                        });

Очевидно, что ничего не делать в основной очереди, но она все еще запаздывает, пока я не прокомментирую строку:

// CGImageRef cgImage = asset.defaultRepresentation.fullScreenImage;

Так что я так растерялся, что-то не так, когда я использовал GCD? Кто-нибудь может помочь мне объяснить это? Любая вещь будет полезна.

Спасибо вам, ребята.

ОБНОВИТЬ

Для @Fogmeister: размер фотографии соответствует размеру полного экрана, размер изображения в актуальном состоянии составляет около половины. Даже я комментирую строку: "imageView.image = image;" это все еще отставание. Что означает, что это не от изменения размера. Я знаю, где берется время, здесь: "asset.defaultRepresentation.fullScreenImage;". Когда я это комментирую, все нормально, лагов больше нет. Итак, что я не понимаю, я уже положил его в фоновом потоке...

4 ответа

Решение

Хорошо, наконец я решил проблему:

Вместо того, чтобы получать изображение напрямую

asset.defaultRepresentation.fullScreenImage

Я использую метод из Apple, например, PhotosByLocation (код ниже), чтобы получить изображение в BG-потоке. Это прекрасно работает, не больше лагов при прокрутке. Но я все еще в замешательстве, я не знаю точно, почему. Поэтому я ценю, если кто-то может мне это объяснить.

- (UIImage *)fullSizeImageForAssetRepresentation:(ALAssetRepresentation *)assetRepresentation {

UIImage *result = nil;
NSData *data = nil;

uint8_t *buffer = (uint8_t *)malloc(sizeof(uint8_t)*[assetRepresentation size]);
if (buffer != NULL) {
    NSError *error = nil;
    NSUInteger bytesRead = [assetRepresentation getBytes:buffer fromOffset:0 length:[assetRepresentation size] error:&error];
    data = [NSData dataWithBytes:buffer length:bytesRead];

    free(buffer);
}

if ([data length]) {
    CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, nil);

    NSMutableDictionary *options = [NSMutableDictionary dictionary];

    [options setObject:(id)kCFBooleanTrue forKey:(id)kCGImageSourceShouldAllowFloat];
    [options setObject:(id)kCFBooleanTrue forKey:(id)kCGImageSourceCreateThumbnailFromImageAlways];
    [options setObject:(id)[NSNumber numberWithFloat:640.0f] forKey:(id)kCGImageSourceThumbnailMaxPixelSize];
    //[options setObject:(id)kCFBooleanTrue forKey:(id)kCGImageSourceCreateThumbnailWithTransform];

    CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options);

    if (imageRef) {
        result = [UIImage imageWithCGImage:imageRef scale:[assetRepresentation scale] orientation:(UIImageOrientation)[assetRepresentation orientation]];
        CGImageRelease(imageRef);
    }

    if (sourceRef) CFRelease(sourceRef);
}

return result;
}

Ваше решение взято из Apple PhotosByLocation на самом деле захватывает изображение с самым большим разрешением, а не полноэкранное изображение. IOW, по сути это то же самое, что вызов fullResolutionImage вместо fullScreenImage. Как это решит вашу проблему, я не уверен. Я борюсь с той же проблемой производительности. Если я использую fullScreenImage, у меня возникают задержки при прокрутке. Но переключение на fullResolutionImage избавляет от лагов. fullResolutionImage занимает примерно вдвое больше времени, чем fullScreenImage, но, поскольку это всегда происходит в фоновом режиме, не должно иметь значения, сколько времени это займет. Я подозреваю, что fullScreenImage возвращает изображение, которое требует какой-то дополнительной обработки, как только оно отображается на экране в главном потоке - отсюда и задержка.

Из яблочной документации:

DISPATCH_QUEUE_PRIORITY_DEFAULT
Items dispatched to the queue run at the default priority; the queue is scheduled for execution after all high priority queues have been scheduled, but before any low priority queues have been scheduled.

DISPATCH_QUEUE_PRIORITY_BACKGROUND
Items dispatched to the queue run at background priority; the queue is scheduled for execution after all high priority queues have been scheduled and the system runs items on a thread whose priority is set for background status. Such a thread has the lowest priority and any disk I/O is throttled to minimize the impact on the system.

Вы запускаете его в отдельном потоке, но это не обязательно поток "в фоновом режиме". Фоновый поток, загружающий что-то в моем опыте, будет полностью заблокирован обновлением пользовательского интерфейса, например прокруткой UIScrollView. Вы пытались использовать DISPATCH_QUEUE_PRIORITY_BACKGROUND?

Вы знаете реальный размер фотографии? Что очень дорого, так это прокрутка изображений, размер которых изменяется по размеру экрана.

Поскольку вы уже загружаете BG-поток, возможно, стоит изменить размер изображения до размера, в котором вы его отображаете, прежде чем вставлять его на экран.

Вы можете увидеть, где идет время, используя инструмент CoreAnimation в Инструментах, профилируя приложение из XCode. Он даже скажет вам, какая строка кода вызывает замедление и пропущенные кадры анимации.

Другие вопросы по тегам