做ios开发的都应该知道SDWebImage,但是自己却一直都是使用没有好好看看源代码,趁热打铁,刚刚看了赶紧记下来!
涉及到的类、类别的相关属性与方法:
UIView+WebCache.h
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock;
SDWebImageCombinedOperation.h s
@interface SDWebImageCombinedOperation : NSObject <SDWebImageOperation>
@property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
@property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock;
@property (strong, nonatomic) NSOperation *cacheOperation; //缓存操作
UIView+WebCacheOperation.h
@property (strong,nonatomic) NSMutableDictionary * operationDictionary; //key="UIImageViewImageLoad"保存id <SDWebImageOperation>的集合 (代码只给了get和set方法)
SDWebImageManager.h//下载、缓存总管理
@property (strong,nonatomic) NSMutableSet *failedURLs;//保存请求失败的url集合
@property (strong,nonatomic) NSMutableArray *runningOperations;
@property (strong,nonatomic,readonly) SDWebImageDownloader *imageDownloader;
@property (strong,nonatomic,readonly) SDImageCache *imageCache;
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionWithFinishedBlock)completedBlock;
SDImageCache.h //缓存管理
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock;
SDWebImageDownloader.h //下载管理
@property (strong,nonatomic) NSOperationQueue *downloadQueue;
SDWebImageDownloaderOperation.h //下载图片的类
@interface SDWebImageDownloaderOperation : NSOperation <SDWebImageOperation>
SDWebImage提供的图片下载缓存最根本的方法就是UIView+WebCache.h类别中的 - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock ;方法!
该方法的功能:
1、[self sd_cancelCurrentImageLoad]; 调用当前UIImageView 的 id <SDWebImageOperation>集合(operationDictionary[@"UIImageViewImageLoad"] as NSArray)中每一项的cancel方法!
2、占位图的的设置与是否有url的判断
3、创建id <SDWebImageOperation>并保存到operationDictionary集合的key="UIImageViewImageLoad"中
SDWebImageManager.h
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
options:(SDWebImageOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageCompletionWithFinishedBlock)completedBlock
1)、检查url是否有效,failedURLs是否包含改url
2)、创建SDWebImageCombinedOperation (operation),添加到operationDictionary集合中
3)、设置当前operation的cacheOperation属性,调用 - (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock方法:
//创建cacheOperation
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {
if (!doneBlock) {
returnnil;
}
if (!key) {
doneBlock(nil, SDImageCacheTypeNone);
returnnil;
}
// 从NSCache中查找图片(key是根据url获取的)
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
doneBlock(image, SDImageCacheTypeMemory);
returnnil;
}
NSOperation *operation = [NSOperation new];
dispatch_async(self.ioQueue, ^{
if (operation.isCancelled) {
return;
}
@autoreleasepool {
// 从diskc磁盘中查找图片,如果存在将图片保存到NSCache中
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage &&self.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
}
dispatch_async(dispatch_get_main_queue(), ^{
doneBlock(diskImage, SDImageCacheTypeDisk);
});
}
});
return operation;
}
//根据key从disk获取图片的过程
- (UIImage *)diskImageForKey:(NSString *)key {
//1、此处从disk中根据key(拼接地址)获取图片对应的NSData
NSData *data = [self diskImageDataBySearchingAllPathsForKey:key];
if (data) {
//2、根据NSData的第一个字节获取对应的图片格式,对不同文件格式的图片进行处理(ImageIO):image/gif(图片集合),image/webp ,png普通图片
UIImage *image = [UIImage sd_imageWithData:data];
//3、根据文件名@2x.或者@3x.来设置图片的scale
image = [self scaledImageForKey:key image:image];
if (self.shouldDecompressImages) {
image = [UIImage decodedImageWithImage:image];
}
return image;
}
else {
returnnil;
}
}
4、执行创建cacheOperation中doneBlock中的代码(核心代码)
1、如果此时获取到图片并且未cancel,则调用completedBlock。如果没有从缓存或者disk中获取到图片则创建一个subOperation进行图片下载处理
此处的。
id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error,BOOL finished) {
//以下是subOperation中(SDWebImageDownloaderOperation的operation实际的下载操作)网络下载完(图片的缓存处理)completedBlock调用与部分下载显示渐进图片(通过ImageIO进行图片的处理进行部分显示类似网站显示大图时慢慢加载完全)的处理
__strong__typeof(weakOperation) strongOperation = weakOperation;
if (!strongOperation || strongOperation.isCancelled) { }
elseif (error) {
dispatch_main_sync_safe(^{
if (strongOperation && !strongOperation.isCancelled) {
completedBlock(nil, error, SDImageCacheTypeNone, finished, url);
}
});
if ( error.code != NSURLErrorNotConnectedToInternet
&& error.code != NSURLErrorCancelled
&& error.code != NSURLErrorTimedOut
&& error.code != NSURLErrorInternationalRoamingOff
&& error.code != NSURLErrorDataNotAllowed
&& error.code != NSURLErrorCannotFindHost
&& error.code != NSURLErrorCannotConnectToHost) {
@synchronized (self.failedURLs) {
[self.failedURLs addObject:url];//下载失败后图片的url保存到failedURLS中
}
}
}else {
if ((options & SDWebImageRetryFailed)) {
@synchronized (self.failedURLs) {
[self.failedURLs removeObject:url];//下载成功后图片的url从failedURLS中移除
}
}
BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
if (options & SDWebImageRefreshCached && image && !downloadedImage) {}
elseif (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0), ^{
UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
if (transformedImage && finished) {
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
[self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk];
}
dispatch_main_sync_safe(^{
if (strongOperation && !strongOperation.isCancelled) {
completedBlock(transformedImage,nil, SDImageCacheTypeNone, finished, url);
}
});
});
}else {
if (downloadedImage && finished) {
[self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
}
dispatch_main_sync_safe(^{
if (strongOperation && !strongOperation.isCancelled) {
completedBlock(downloadedImage,nil, SDImageCacheTypeNone, finished, url);
}
});
}
}
if (finished) {
@synchronized (self.runningOperations) {
if (strongOperation) {
[self.runningOperations removeObject:strongOperation];
}
}
}
}];
//operation cancel时的操作
operation.cancelBlock = ^{
[subOperation cancel];
@synchronized (self.runningOperations) {
__strong__typeof(weakOperation) strongOperation = weakOperation;
if (strongOperation) {
[self.runningOperations removeObject:strongOperation];
}
}
};
//创建subOpertaion的方法中会调用该方法,实际创建创建自定义NSOperation的操作在createCallback中
- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageNoParamsBlock)createCallback {
if (url ==nil) {
if (completedBlock !=nil) {
completedBlock(nil,nil,nil,NO);
}
return;
}
dispatch_barrier_sync(self.barrierQueue, ^{
BOOL first =NO;
if (!self.URLCallbacks[url]) {
self.URLCallbacks[url] = [NSMutableArray new];
first =YES;
}
// 根据url作为key获取是否存在有对应的progressBlock与completedBlock记录则说明已经创建了一个对应url的自定义个NSOperation,则不创建新的自定义NSOperation,否则创建一个新的自定义NSOperation
NSMutableArray *callbacksForURL =self.URLCallbacks[url];
NSMutableDictionary *callbacks = [NSMutableDictionary new];
if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
[callbacksForURL addObject:callbacks];
self.URLCallbacks[url] = callbacksForURL;
if (first) {
createCallback();
}
});
}
5、实际下载图片的操作类中图片渐进显示的操作(还是使用NSURLConnection来现实的下载):
SDWebImageDownloaderOperation.h
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.imageData appendData:data];
if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize >0 &&self.completedBlock) {
const NSInteger totalSize =self.imageData.length;
CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)self.imageData,NULL);
//获取图片的宽、高
if (width + height ==0) {
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource,0,NULL);
if (properties) {
NSInteger orientationValue = -1;
CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);
if (val) CFNumberGetValue(val, kCFNumberLongType, &height);
val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth);
if (val) CFNumberGetValue(val, kCFNumberLongType, &width);
val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation);
if (val) CFNumberGetValue(val, kCFNumberNSIntegerType, &orientationValue);
CFRelease(properties);
orientation = [[self class] orientationFromPropertyValue:(orientationValue == -1 ?1 : orientationValue)];
}
}
if (width + height >0 && totalSize <self.expectedSize) {
// 根据ImageIO获取图片中的第一张图片(gif是多张图片)
CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource,0,NULL);
//很费解为什么要去绘制一张图片,此处已经拿到了渐进显示的图片了(针对大图片的处理吗?)
#ifdef TARGET_OS_IPHONE
// Workaround for iOS anamorphic image(解决失真问题)
if (partialImageRef) {
const size_t partialHeight = CGImageGetHeight(partialImageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width *4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorSpace);
if (bmContext) {
CGContextDrawImage(bmContext, (CGRect){.origin.x =0.0f, .origin.y =0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef);
CGImageRelease(partialImageRef);
partialImageRef = CGBitmapContextCreateImage(bmContext);
CGContextRelease(bmContext);
}else {
CGImageRelease(partialImageRef);
partialImageRef =nil;
}
}
#endif
//图片转换
if (partialImageRef) {
UIImage *image = [UIImage imageWithCGImage:partialImageRef scale:1 orientation:orientation];
NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
UIImage *scaledImage = [self scaledImageForKey:key image:image];
if (self.shouldDecompressImages) {
image = [UIImage decodedImageWithImage:scaledImage];
}else {
image = scaledImage;
}
CGImageRelease(partialImageRef);
dispatch_main_sync_safe(^{
if (self.completedBlock) {
self.completedBlock(image,nil,nil,NO);
}
});
}
}
CFRelease(imageSource);
}
if (self.progressBlock) {
self.progressBlock(self.imageData.length,self.expectedSize);
}
}
几乎就这么多内容了,写的不是很好结合源代码会更好理解!