我有一个UITableView
,大约有10个子类UITableViewCell
名为TBPostSnapCell。初始化时,每个单元格设置两个变量,UIImage
通过GCD
下载,或从存储在用户文档目录中的缓存中检索。
出于某种原因,这会导致tableView明显滞后,从而破坏应用程序的用户体验。表
请告诉我如何减少这种滞后?
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);
});
});
}
答案 0 :(得分:2)
首先要尝试将DISPATCH_QUEUE_PRIORITY_HIGH(也称为最重要的工作,永远忘记一切)转换为DISPATCH_QUEUE_PRIORITY_LOW。
如果这不能解决问题,你可以尝试通过dispatch_sources进行http流量,但这需要做很多工作。
你也可能只是试图限制使用信号量的飞行中http提取的数量,真正的诀窍是决定最佳限制是什么,因为“好”数字将取决于网络,你的CPU和内存压力。也许基准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回调重叠。
顺便说一句,你做了很多文件I / O,它在闪存上比任何磁盘都要快,但是如果你仍然在寻找可能是另一个攻击场所的性能获胜。例如,可能会将UIImage保留在内存中,直到您收到内存不足警告,然后再将其写入磁盘。