SDWebImage学习
2015-08-31 23:16
344 查看
SDWebImage学习
github托管地址:https://github.com/rs/SDWebImage导入头文件:UIImageView+WebCache.h
使用sd_setImageWithURL:缓存图片:
1 . 方法sd_setImageWithURL://简单的加载url中的图片 [self.image sd_setImageWithURL:url]
2 . 方法 sd_setImageWithURL: placeholderImage:
//加载完成之前使用图片placeImage作为默认值 [self.image1 sd_setImageWithURL:url placeholderImage:placeImage];
3 . 方法sd_setImageWithURL:imagePath2 completed:
//在图片加载完成之后执行block [self.image2 sd_setImageWithURL:imagePath2 completed: ^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { NSLog(@"Do something in here..."); }];
4 . 方法sd_setImageWithURL:placeholderImage:options:
//options表示缓存方式 [self.image sd_setImageWithURL:url placeholderImage:placeImage options:SDWebImageRetryFailed];
options每个选项含义: //加载失败后重试 SDWebImageRetryFailed = 1 << 0, //UI交互期间延时下载 SDWebImageLowPriority = 1 << 1, //只进行内存缓存 SDWebImageCacheMemoryOnly = 1 << 2, //逐步下载,显示图像也是逐步的 SDWebImageProgressiveDownload = 1 << 3, //刷新缓存 SDWebImageRefreshCached = 1 << 4, //后台下载 SDWebImageContinueInBackground = 1 << 5, //通过设置NSMutableURLRequest.HTTPShouldHandleCookies = YES //把cookies存储在NSHTTPCookieStore SDWebImageHandleCookies = 1 << 6, //允许使用无效的SSL证书 SDWebImageAllowInvalidSSLCertificates = 1 << 7, //优先下载 SDWebImageHighPriority = 1 << 8, //延迟显示占位符 SDWebImageDelayPlaceholder = 1 << 9, //进行任意图形变换 SDWebImageTransformAnimatedImage = 1 << 10,
SDWebImage中我们使用较多的是它提供的UIImageView分类,支持从远程服务器下载并缓存图片。从iOS5.0开始,NSURLCache也可以处理磁盘缓存,那么SDWebImage有什么优势?首先,NSURLCache是缓存原始数据(raw data)到磁盘或内存,因此每次使用的时候需要将原始数据转换成具体的对象,如UIImage等,这会导致额外的数据解析以及内存占用等,而SDWebImage则是缓存UIImage对象在内存,缓存在NSCache中,同时直接保存压缩过的图片
4000
到磁盘中;其次,当你第一次在UIImageView中使用image对象的时候,图片的解码是在主线程中运行的!而SDWebImage会强制将解码操作放到子线程中。下图是SDWebImage简单的类图关系:
图片加载调用函数:
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock { //取消正在下载的操作 [self sd_cancelCurrentImageLoad]; //创建对象self和对象url的关联 objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); /*...*/ if (url) { //防止循环引用 __weak UIImageView *wself = self; //由SDWebImageManager负责图片的获取 id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed: ^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { /*获取图片到主线层显示*/ }]; [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"]; } else { /*...*/ } }
由以上函数可知图片是从服务端、内存或者硬盘获取是由SDWebImageManager管理的,这个类有几个重要的属性:
//负责管理cache,涉及内存缓存和硬盘保存 @property (strong, nonatomic, readonly) SDImageCache *imageCache; //负责从网络下载图片 @property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader;
manager会根据URL先去imageCache中查找对应的图片,如果没有再使用downloader去下载,并在下载完成缓存图片到imageCache,接着看实现:
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionWithFinishedBlock)completedBlock { /*...*/ //根据URL生成对应的key,没有特殊处理为[url absoluteString]; NSString *key = [self cacheKeyForURL:url]; //去imageCache中寻找图片 operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) { /*...*/ //如果图片没有找到,或者采用的SDWebImageRefreshCached选项,则从网络下载 if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector( imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) { if (image && options & SDWebImageRefreshCached) { dispatch_main_sync_safe(^{ //如果图片找到了,但是采用的SDWebImageRefreshCached选项, //通知获取到了图片,并再次从网络下载,使NSURLCache重新刷新 completedBlock(image, nil, cacheType, YES, url); }); } /*下载选项设置...*/ //使用imageDownloader开启网络下载 id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) { /*...*/ if (downloadedImage && finished) { //下载完成后,先将图片保存到imageCache中,然后主线程返回 recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk]; } dispatch_main_sync_safe(^{ if (!weakOperation.isCancelled) { completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url); } }); } } /*...*/ else if (image) { //在cache中找到图片了,直接返回 dispatch_main_sync_safe(^{ if (!weakOperation.isCancelled) { completedBlock(image, nil, cacheType, YES, url); } }); /*...*/ } }]; return operation; }
下面先看downloader从网络下载的过程,下载是放在NSOperationQueue中进行的,默认maxConcurrentOperationCount为6,timeout时间为15s:
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock { __block SDWebImageDownloaderOperation *operation; __weak SDWebImageDownloader *wself = self; /*...*/ //防止NSURLCache和SDImageCache重复缓存 NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval]; request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies); request.HTTPShouldUsePipelining = YES; if (wself.headersFilter) { request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]); } else { request.allHTTPHeaderFields = wself.HTTPHeaders; } //SDWebImageDownloaderOperation派生自NSOperation,负责图片下载工作 operation = [[wself.operationClass alloc] initWithRequest:request options:options progress:^(NSInteger receivedSize, NSInteger expectedSize) {/*...*/} completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {/*...*/} cancelled:^{/*...*/}]; /*...*/}]; return operation; }
SDWebImageDownloaderOperation派生自NSOperation,通过NSURLConnection进行图片的下载,为了确保能够处理下载的数据,需要在后台运行runloop:
- (void)start { @synchronized (self) { /*...*/ #if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 /*开启后台下载*/ if ([self shouldContinueWhenAppEntersBackground]) { __weak __typeof__ (self) wself = self; self.backgroundTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ __strong __typeof (wself) sself = wself; if (sself) { [sself cancel]; [[UIApplication sharedApplication] endBackgroundTask:sself.backgroundTaskId]; sself.backgroundTaskId = UIBackgroundTaskInvalid; } }]; } #endif self.executing = YES; self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; self.thread = [NSThread currentThread]; } [self.connection start]; if (self.connection) { if (self.progressBlock) { self.progressBlock(0, NSURLResponseUnknownLength); } /*广播通知*/ [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self]; if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_5_1) { CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false); } else { //在默认模式下运行当前runlooprun,直到调用CFRunLoopStop停止运行 CFRunLoopRun(); } if (!self.isFinished) { [self.connection cancel]; [self connection:self.connection didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo: @{NSURLErrorFailingURLErrorKey : self.request.URL}]]; } } else { if (self.completedBlock) { self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo: @{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES); } } #if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 if (self.backgroundTaskId != UIBackgroundTaskInvalid) { [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskId]; self.backgroundTaskId = UIBackgroundTaskInvalid; } #endif }
下载过程中,在代理 - (void)connection:(NSURLConnection )connection didReceiveData:(NSData )data中将接收到的数据保存到NSMutableData中,[self.imageData appendData:data],下载完成后在该线程完成图片的解码,并在完成的completionBlock中进行imageCache的缓存:
- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection { SDWebImageDownloaderCompletedBlock completionBlock = self.completedBlock; @synchronized(self) { //停止当前的runLoop CFRunLoopStop(CFRunLoopGetCurrent()); /*...*/ } /*...*/ if (compl ada5 etionBlock) { if (self.options & SDWebImageDownloaderIgnoreCachedResponse && responseFromCached) { completionBlock(nil, nil, nil, YES); } else { UIImage *image = [UIImage sd_imageWithData:self.imageData]; NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL]; image = [self scaledImageForKey:key image:image]; if (!image.images) { //图片解码 image = [UIImage decodedImageWithImage:image]; } if (CGSizeEqualToSize(image.size, CGSizeZero)) { completionBlock(nil, nil, [NSError errorWithDomain:@"SDWebImageErrorDomain" code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}], YES); } else { completionBlock(image, self.imageData, nil, YES); } } } self.completionBlock = nil; [self done]; }
相关文章推荐
- iOS开发实战:SDWebImage深度解析
- SDWebImage使用——一个可管理远程图片加载的类库
- sdwebImage 如何清除缓存
- iOS多线程编程及简单封装图片下载
- Xcode6编译SDWebImage报错解决方法(SDWebImageDownloaderOperation.m错误)
- iOS开发系列--网络开发
- SDWebImage
- SDWebImage内部实现过程
- SDWebImage使用入门
- SDWebImage源码解析之SDWebImageManager的注解
- iOS网络编程(三) 异步加载及缓存图片---->SDWebImage
- SDWebImage手动清除缓存的方法
- SDWebImage缓存
- SDWebImage关于地址https 带有 ssl证书的图片加载
- 用SDWebImage开源库异步下载图片
- sdwebimage缓存图片对比问题
- 第三方框架SDWebImage的使用
- SDWebImage类实现图片的下载
- iOS_第3方类库SDWebImage
- IOS网络图片缓存详解