我从亚马逊的Web服务下载图像以获取UITableView。我还使用单例来缓存最近的图像。我想知道如何最好地处理一种情况,例如,我在视图中有两个单元格,并且两个实际上都使用相同的键(如果使用NSURLSession,URL将是相同的情况)。显然不需要两次下载图像,但检查缓存不会返回结果,因为第二次下载开始时第一次下载将不会完成。我目前的逻辑是使用一个额外的NSDictionary单例,它将包含当前正在下载但不完整的每个图像的密钥。检查缓存后我会检查一下,如果密钥存在,我会将UIImageView的引用添加到密钥的数组中 - 当这个下载完成后,这个数组就可以用来更新所有的UIImageViews在数组中引用。这不完全是一个"清洁"在我看来这样做的方法 - 是否有更好的处理方法?虽然这个问题更具概念,但这是我目前的代码:
custom_image_view.m
-(void)set_image:(NSString *)key desired_size:(CGSize)desired_size
{
singleton *caches = [singleton instance];
if (!key.length || [key isEqualToString:@"0"])
{
return;
}
if (![caches is_file_cached:key type:IMAGE_CACHE])
{
//should i check a new dictionary here to see if it's currently downloading?
//if downloading, add self to array and return
//if not downloading, make new key
NSString *bucket = @"bucket.mysite.com";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documents_directory = [paths objectAtIndex:0];
NSString *image_path =[documents_directory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.jpg",@"cached"]];
//only relevant for AWS users
AWSStaticCredentialsProvider *credentialsProvider = [AWSStaticCredentialsProvider credentialsWithAccessKey:@"accesskey" secretKey:@"secretkey"];
AWSServiceConfiguration *configuration = [AWSServiceConfiguration configurationWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
download_request = [AWSS3GetObjectRequest new];
download_request.bucket = bucket;
download_request.key = key;
download_request.downloadingFileURL = [NSURL fileURLWithPath:image_path];
AWSS3 *transferManager = [[AWSS3 alloc] initWithConfiguration:configuration];
NSLog(@"manager: %@",transferManager);
//begin request
[[transferManager getObject:download_request] continueWithBlock:^id(BFTask *task)
{
if (task.error != nil)
{
NSLog(@"error");
if(task.error.code != AWSS3TransferManagerErrorCancelled && task.error.code != AWSS3TransferManagerErrorPaused)
{
NSLog(@"error code: %@",task.error);
NSLog(@"key: %@",key);
}
}
else
{
//successful download
self->download_request = nil;
dispatch_async(dispatch_get_main_queue(), ^
{
NSData *data = [NSData dataWithContentsOfFile:image_path];
UIImage *image = [UIImage imageWithData:data];
if (image != nil)
{
self.image = [self crop_image:image to_size:desired_size];
//loop through new dictionary's array and assign image to each as well?
[caches add_file_to_cache:key withData:data type:IMAGE_CACHE];
}
});
}
return nil;
}];
}
else
{
dispatch_async(dispatch_get_main_queue(), ^
{
NSData *data = [caches get_cached_data:key type:IMAGE_CACHE];
UIImage *image = [UIImage imageWithData:data];
self.image = [self crop_image:image to_size:desired_size];
});
}
}
答案 0 :(得分:1)
BoltsFramework让你的这项任务变得更轻松,但你仍然需要一些缓存。
getObject:
返回BFTask
。 continueWithBlock:
是BFTask
上的一个方便的方法,它将在任务完成后执行一个块。它也已经能够通过相同的任务完成属性来执行多个块,从而为您节省了大量的工作。剩下的就是将整个事物拉到一起只是从image_path到BFTask
的简单缓存。这里有一些代码可以更好地解释。
某处:
static NSMutableDictionary * _imagePathToTask = [NSMutableDictionary new];
而不是:
[[transferManager getObject:download_request] continueWithBlock:^id(BFTask *task) {
做这样的事情:
BFTask *task = [_imagePathToTask objectForKey:image_path];
if (task == nil) {
task = [transferManager getObject:download_request];
[_imagePathToTask setObject:task forKey:image_path];
}
[task continueWithBlock:^id(BFTask *task) {
即使任务完成,也会使用该任务调用您的块。因此,只要您的词典存在,您的图像请求都可以被发送,因为您知道您不会两次下载相同的图像。
现在唯一最重要的是粗略的是我们如何存储字典。更好的方法可能是为AWSS3
创建一个类别,为您返回文件路径的BFTask。这样做会使您的实现回到UIImageView
类别中相同数量的代码。您可以使用associated object在AWSS3实例上存储image_path->任务字典。我想你无论如何都可能将其中一个包装起来重用,但也许你从代码中省略了它以保持你的例子简短。
编辑:
但我想更直接地回答你的问题,你有正确的想法。如果您查看BFTask和回调NSMutableArray
,他们就会按照您的描述进行操作。最大的不同是应用程序中的级别。
答案 1 :(得分:0)
使用GitHub上提供的SDWebImage访问https://github.com/rs/SDWebImage。 通过它,我在我的项目中使用它在相同的情况下它有用。 该库为UIImageView提供了一个类别,支持来自Web的远程图像。 它基本上是异步映像下载器,具有UIImageView类别的缓存支持。