SDWebImage自我认识

做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 (assignnonatomicgetter = isCancelled) BOOL cancelled;

@property (copynonatomic) SDWebImageNoParamsBlock cancelBlock;

@property (strongnonatomic) NSOperation *cacheOperation; //缓存操作


UIView+WebCacheOperation.h

@property (strong,nonatomic) NSMutableDictionary * operationDictionary; //key="UIImageViewImageLoad"保存id <SDWebImageOperation>的集合 (代码只给了getset方法)


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;

}


//根据keydisk获取图片的过程

- (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中(SDWebImageDownloaderOperationoperation实际的下载操作)网络下载完(图片的缓存处理)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];//下载成功后图片的urlfailedURLS中移除

            }

        }

       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获取是否存在有对应的progressBlockcompletedBlock记录则说明已经创建了一个对应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);

    }

}

几乎就这么多内容了,写的不是很好结合源代码会更好理解!






>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值