第一种:
这是我自己封装的一个异步下载图片的下载器。
实现原理:
1.请求服务器数据,加载本地的占位图片
2.根据链接查找本地是否已经有缓存,有就直接加载
3.本地没有缓存,就去下载
待完善:
1.今后将代理改为Block来 实现,代码的易读性更强
使用:
// 下载图片 Download *ADownload = [[Download alloc]init]; ADownload.delegate = self; [ADownload DownloadURL:tempString1 ImageID:
.h
#import <Foundation/Foundation.h> // 不建议这样缓存文件目录
#define kDiskCachePath [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/imageCache"] @protocol DownloadDelete; //下载文件到本地 @interface Download : NSObject @property (nonatomic, weak) id <DownloadDelete> delegate; //下载文件到本地 -(void)DownloadURL:(NSString *)ADownloadURL ImageID:(NSInteger)AImageID; @end @protocol DownloadDelete <NSObject> @required //下载成功 -(void)DownloadIsSuccess:(NSData *)AImageData ImageID:(NSInteger)AImageID; //下载失败 -(void)DownloadIsFail:(NSString *)AFailMsgString ImageID:(NSInteger)AImageID; @end
#import "Download.h" @interface Download () { } @property (nonatomic, retain) NSData *FImageData; @end @implementation Download @synthesize delegate; @synthesize FImageData; //下载文件到本地 -(void)DownloadURL:(NSString *)ADownloadURL ImageID:(NSInteger)AImageID { if (![[NSFileManager defaultManager] fileExistsAtPath:kDiskCachePath]) {//如果目录imageCache不存在,创建目录 NSError *error=nil; [[NSFileManager defaultManager] createDirectoryAtPath:kDiskCachePath withIntermediateDirectories:YES attributes:nil error:&error]; } // 当图片的url为nil,会崩溃,需要处理一下
NSString *AImageNameString = [ADownloadURL lastPathComponent]; //临时修改 添加 NSString *AImagePath = [kDiskCachePath stringByAppendingFormat:@"/%@",AImageNameString]; // [AImageView setImage:[UIImage imageNamed:[NSString stringWithFormat:@"img%ld",APositionID + 1]]]; //如果有缓存图片,直接读取cache内的缓存图片 if ([[NSFileManager defaultManager] fileExistsAtPath:AImagePath]) { NSData *data = [NSData dataWithContentsOfFile:AImagePath]; [delegate DownloadIsSuccess:data ImageID:AImageID]; } else {//如果没有缓存图片,异步加载网络图片 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ NSString *AEncodingDownloadURL = [ADownloadURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSURL *AImagePathURL = [[NSURL alloc] initWithString:AEncodingDownloadURL]; NSData *AImageData = [[NSData alloc] initWithContentsOfURL:AImagePathURL]; if(AImageData) { FImageData = AImageData; [[NSFileManager defaultManager] createFileAtPath:AImagePath contents:AImageData attributes:nil]; } else { } }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ if (FImageData.length == 0) { //下载失败 [delegate DownloadIsFail:@"图片下载失败" ImageID:AImageID]; } else { [delegate DownloadIsSuccess:FImageData ImageID:AImageID]; } }); } }
第二种:
思路:
1.先设置图片的URL,并设置站位图片
2.重复下载,移除当前的操作
3.启动下载器去下载图片
4.判断内存中是否存在缓存,再判断沙盒中是否没有缓存,有直接返回,结束当前的操作,否则执行下载
5.下载完成移除相关的操作,并将图片缓存到内容中
// 图片的分类文件 #define JQCurrentURLString @"currentURLStirng" @interface UIImageView () /** * 记录当前正在下载的URL */ @property (nonatomic,copy)NSString *currentURLStirng; @end @implementation UIImageView (JQWebImage) - (void)setImageWithURLString:(NSString *)URLString{ //如果对同一个cell执行了两次请求操作,取消掉当前正在执行的 if (![URLString isEqualToString:self.currentURLStirng]&&self.currentURLStirng) { [[JQDownloadImageManager sharedManager] cancelDownload:self.currentURLStirng]; } //通过下载管理器去调用相应的下载操作,通过complete 实现block回调 [[JQDownloadImageManager sharedManager] downloadImageWithURLString:URLString complete:^(UIImage *image) { self.image = image; }]; //记录正在下载的url self.currentURLStirng = URLString; } /** * 通过runtime动态添加属性 */ - (void)setCurrentURLStirng:(NSString *)currentURLStirng{ objc_setAssociatedObject(self, JQCurrentURLString, currentURLStirng, OBJC_ASSOCIATION_COPY_NONATOMIC); } - (NSString *)currentURLStirng{ return objc_getAssociatedObject(self, JQCurrentURLString); } @end // 下载器 @interface JQDownloadImageManager() /** * 操作缓存 */ @property (nonatomic,strong)NSMutableDictionary *operationCache; /** * 操作队列(下载队列)全局队列好处:可以控制并发数 */ @property (nonatomic,strong)NSOperationQueue *queue; /** * 内存缓存,把下载好的图片保存到内存中 */ @property (nonatomic,strong)NSMutableDictionary *imageCache; @end @implementation JQDownloadImageManager + (instancetype)sharedManager{ static dispatch_once_t onceToken; //只有一个实例 static XZHDownloadImageManager *manager; dispatch_once(&onceToken, ^{ manager = [[self alloc]init]; }); return manager; } /** * 通过URLString下载并指定回调操作 */ - (void)downloadImageWithURLString:(NSString *)URLString complete:(void(^)(UIImage *image))complete{ if ([self.operationCache objectForKey:URLString]) { NSLog(@"正在下载"); return; } //开始下载前判断有没有缓存 if ([self checkCache:URLString]) { UIImage *image = [self.imageCache objectForKey:URLString]; complete(image); return; } //创建下载操作 //执行异步下载图片 JQOperation *op = [XZHOperation downloadImageOperationWithURLString:URLString downloadFinished:^(UIImage *image) { //下载完成移除操作 [self.operationCache removeObjectForKey:URLString]; //回传图片 complete(image); }]; //把操作添加到队列中 [self.queue addOperation:op]; //把操作添加到操作缓存中 [self.operationCache setObject:op forKey:URLString]; } /** * 取消指定图片的下载操作 * */ - (void)cancelDownload:(NSString *)URLString{ //通过url拿到操作 [self.operationCache[URLString] cancel]; //从缓存中移除 [self.operationCache removeObjectForKey:URLString]; } - (BOOL)checkCache:(NSString *)URLStirng{ //有内存缓存,直接返回。否则从沙盒取,再保存到内存中,最后还是从内存取 //先判断内存 if ([self.imageCache objectForKey:URLStirng]) { NSLog(@"内存缓存"); return YES; } //判断沙盒 NSString *path = [URLStirng appendCaches]; UIImage *image = [UIImage imageWithContentsOfFile:path]; if (image) { NSLog(@"沙盒缓存"); //把图片保存到内存缓存中 [self.imageCache setObject:image forKey:URLStirng]; return YES; } return NO; } #pragma mark -数据懒加载 - (NSOperationQueue *)queue{ if (_queue==nil) { _queue = [[NSOperationQueue alloc]init]; } return _queue; } - (NSMutableDictionary *)operationCache{ if (_operationCache==nil) { _operationCache = [NSMutableDictionary dictionary]; } return _operationCache; } - (NSMutableDictionary *)imageCache{ if (_imageCache == nil) { _imageCache = [NSMutableDictionary dictionary]; } return _imageCache; } @end // 下载的操作 @interface JQOperation() /** * 下载图片的URL */ @property (nonatomic,copy)NSString *URLString; /** * 下载完成后异步回传下载好的图片 */ @property (nonatomic,copy)void (^downloadFinished)(UIImage *image); @end @implementation JQOperation /** * 封装内部属性,通过提供的类方法传入所需的值赋给相应的属性 * */ + (instancetype)downloadImageOperationWithURLString:(NSString *)URLStirng downloadFinished:(void(^)(UIImage *image))downloadFinished{ JQOperation *download = [[self alloc]init]; download.URLString = URLStirng; download.downloadFinished = downloadFinished; return download; } /** * 通过重写main方法来干涉操作的内部,从而实现中断/取消下载 */ - (void)main{ //通过包装一个自动释放池可以包装整个操作内存峰值不会太高 @autoreleasepool { //下载图片 [NSThread sleepForTimeInterval:1]; NSURL *url = [NSURL URLWithString:self.URLString]; NSData *data = [NSData dataWithContentsOfURL:url]; //把二进制数据转换成图片 UIImage *image = [UIImage imageWithData:data]; //把图片保存到沙盒中 if (data) { [data writeToFile:(写入的路径) atomically:YES]; } //在关键点(比较耗时的地方)盘点是否在下载期间已经取消下载了 //取消下载,直接返回 if(self.isCancelled){ NSLog(@"已经取消下载"); return; } //回到主线程刷新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ //判断回调block是否已经被赋值 if (self.downloadFinished) { self.downloadFinished(image); } }]; } } /** * start方法一直都会被调用,无论是否被取消 */ //- (void)start{ // //} @end