Уменьшите отставание с помощью UITableView и GCD - PullRequest
0 голосов
/ 02 декабря 2011

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

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

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

tableView ... cellForRowAtIndexPath:

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

1 Ответ

2 голосов
/ 02 декабря 2011

Первое, что нужно попробовать, - это преобразовать DISPATCH_QUEUE_PRIORITY_HIGH (он же ONG MOST ВАЖНАЯ РАБОТА, КОГДА-ЛИБО ЗАБЫТЬ ВСЕ ЕЩЕ) в что-то вроде DISPATCH_QUEUE_PRIORITY_LOW.

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

Вы также можете просто попытаться ограничить число 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);
    });
});

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...