Уменьшить задержку с UitableView и GCD
-
27-10-2019 - |
Вопрос
у меня есть UITableView
состоящий из примерно 10 подклассных UITableViewCell
S назван TBPOSTSNAPCELL. Каждая ячейка при инициативе устанавливает две свои переменные с UIImage
S скачал через 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 в памяти, пока вы не получите низкое предупреждение о памяти и только затем напишите их на диск.