Вопрос

у меня есть UITableView состоящий из примерно 10 подклассных UITableViewCellS назван TBPOSTSNAPCELL. Каждая ячейка при инициативе устанавливает две свои переменные с UIImageS скачал через GCD или извлечен из кэша, хранящегося в каталоге документов пользователя.

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

Пожалуйста, вы можете сказать мне, как я могу уменьшить эту задержку?

TableView ... cellforrowroryAtIndexPath:

if (post.postType == TBPostTypeSnap || post.snaps != nil) {

        TBPostSnapCell *snapCell = (TBPostSnapCell *) [tableView dequeueReusableCellWithIdentifier:snapID];

        if (snapCell == nil) {

            snapCell = [[[NSBundle mainBundle] loadNibNamed:@"TBPostSnapCell" owner:self options:nil] objectAtIndex:0];

            [snapCell setPost:[posts objectAtIndex:indexPath.row]];

            [snapCell.bottomImageView setImage:[UIImage imageNamed:[NSString stringWithFormat:@"%d", (indexPath.row % 6) +1]]];
        }

    [snapCell.commentsButton setTag:indexPath.row];
    [snapCell.commentsButton addTarget:self action:@selector(comments:) forControlEvents:UIControlEventTouchDown];
    [snapCell setSelectionStyle:UITableViewCellSelectionStyleNone];

    return snapCell;
}

Tbsnapcell.m

- (void) setPost:(TBPost *) _post {

    if (post != _post) {
        [post release];
        post = [_post retain];
    }
    ...

    if (self.snap == nil) {

        NSString *str = [[_post snaps] objectForKey:TBImageOriginalURL];
        NSURL *url = [NSURL URLWithString:str];
        [TBImageDownloader downloadImageAtURL:url completion:^(UIImage *image) {
            [self setSnap:image];
        }];
    }

    if (self.authorAvatar == nil) {
        ...
        NSURL *url = [[[_post user] avatars] objectForKey:[[TBForrstr sharedForrstr] stringForPhotoSize:TBPhotoSizeSmall]];

        [TBImageDownloader downloadImageAtURL:url completion:^(UIImage *image) {
            [self setAuthorAvatar:image];
        }];
        ...
    }

}

Tbimagedownloader.m

+ (void) downloadImageAtURL:(NSURL *)url completion:(TBImageDownloadCompletion)_block {

    if ([self hasWrittenDataToFilePath:filePathForURL(url)]) {
        [self imageForURL:filePathForURL(url) callback:^(UIImage * image) {
            _block(image); //gets UIImage from NSDocumentsDirectory via GCD
        }];
        return;
    }

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_async(queue, ^{
        UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
        dispatch_async(dispatch_get_main_queue(), ^{
            [self writeImageData:UIImagePNGRepresentation(image) toFilePath:filePathForURL(url)];
            _block(image);
        });
    });
}
Это было полезно?

Решение

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

Если это не исправить, вы можете попытаться сделать HTTP -трафик через Dispatch_sources, но это большая работа.

Вы также можете просто попытаться ограничить количество выборов в полете HTTP с помощью семафора, настоящий трюк будет решать, каков наилучший предел, поскольку «хороший» номер будет зависеть от сети, ваших процессоров и давления памяти. Возможно, тесты 2, 4 и 8 с несколькими конфигурациями и посмотрите, достаточно ли шаблона для обобщения.

Хорошо, давайте попробуем только один, замените queue = ... с:

static dispatch_once_t once;
static dispatch_queue_t queue = NULL;
dispatch_once(&once, ^{
    queue = dispatch_queue_create("com.blah.url-fetch", NULL);
});

Оставьте остальную часть кода как есть. Это, вероятно, будет наименее размахиванием, но может не загружать изображения очень быстро.

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

dispatch_async(queue, ^{
    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
    dispatch_async(dispatch_get_main_queue(), ^{
        [self writeImageData:UIImagePNGRepresentation(image) toFilePath:filePathForURL(url)];
        _block(image);
    });
});

Заменив его:

static dispatch_once_t once;
static const int max_in_flight = 2;  // Also try 4, 8, and maybe some other numbers
static dispatch_semaphore_t limit = NULL;
dispatch_once(&once, ^{
    limit = dispatch_semaphore_create(max_in_flight);
});
dispatch_async(queue, ^{
    dispatch_semaphore_wait(limit, DISPATCH_TIME_FOREVER);
    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
    //  (or you might want the dispatch_semaphore_signal here, and not below)
    dispatch_async(dispatch_get_main_queue(), ^{
        [self writeImageData:UIImagePNGRepresentation(image) toFilePath:filePathForURL(url)];
        _block(image);
        dispatch_semaphore_signal(limit);
    });
});

ПРИМЕЧАНИЕ: Я не проверял ни одного из этого кода, даже чтобы увидеть, компилируется ли он. Как написано, это позволит только 2 потока выполнять основную часть кода в двух вложенных блоках. Возможно, вы захотите перенести Dispatch_semaphore_signal в договорную строку. Это ограничит вас двумя избраниями/изображением, но им будет разрешено перекрываться с написанием данных изображения в файл и вызовом вашего обратного вызова _block.

Кстати, вы делаете много файлов ввода -вывода, который быстрее на Flash, тогда любой диск когда -либо был, но если вы все еще ищете победы, которые могут быть еще одним местом для атаки. Например, может быть, сохранить UIImage в памяти, пока вы не получите низкое предупреждение о памяти и только затем напишите их на диск.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top