SDWebImage是iOS開發中主流的圖像加載庫,它幫咱們處理內存緩存、磁盤緩存與及圖像加載的一系列操做。使用起來方便快捷,讓咱們更好的專一於業務邏輯的開發。html
SDWebImage框架組成以下:web
功能快速一覽圖:objective-c
SDWebImageCompat
作機型適配的。SDWebImageManager
管理緩存和下載的一個類。SDImageCache
處理緩存和內存的類。SDWebImageDownloader
異步下載器專用和優化圖像加載。SDWebImagePrefetcher
圖片的預加載。緩存
SDImageCache
是繼承自NSObject
的。作了cache的一些基本配置和cache的管理。
如cache的大小,cache的有效期,添加cache,刪除cache等。架構
cache的類型以下:框架
typedef NS_ENUM(NSInteger, SDImageCacheType) { /** * The image wasn't available the SDWebImage caches, but was downloaded from the web.(不緩存,從web加載) */ SDImageCacheTypeNone, /** * The image was obtained from the disk cache.(磁盤緩存) */ SDImageCacheTypeDisk, /** * The image was obtained from the memory cache.(內存緩存) */ SDImageCacheTypeMemory };
內部的AutoPurgeCache
是繼承自NSCache
的,主要用途是在收到系統的UIApplicationDidReceiveMemoryWarningNotification通知時,清理內存緩存。異步
此外,SDWebCache
的內存緩存也是使用的NSCache
.
設置緩存的核心方法以下:async
- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk { if (!image || !key) { return; } // if memory cache is enabled if (self.shouldCacheImagesInMemory) { NSUInteger cost = SDCacheCostForImage(image); [self.memCache setObject:image forKey:key cost:cost]; } if (toDisk) { dispatch_async(self.ioQueue, ^{ NSData *data = imageData; if (image && (recalculate || !data)) { #if TARGET_OS_IPHONE // We need to determine if the image is a PNG or a JPEG // PNGs are easier to detect because they have a unique signature (http://www.w3.org/TR/PNG-Structure.html) // The first eight bytes of a PNG file always contain the following (decimal) values: // 137 80 78 71 13 10 26 10 // If the imageData is nil (i.e. if trying to save a UIImage directly or the image was transformed on download) // and the image has an alpha channel, we will consider it PNG to avoid losing the transparency int alphaInfo = CGImageGetAlphaInfo(image.CGImage); BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone || alphaInfo == kCGImageAlphaNoneSkipFirst || alphaInfo == kCGImageAlphaNoneSkipLast); BOOL imageIsPng = hasAlpha; // But if we have an image data, we will look at the preffix if ([imageData length] >= [kPNGSignatureData length]) { imageIsPng = ImageDataHasPNGPreffix(imageData); } if (imageIsPng) { data = UIImagePNGRepresentation(image); } else { data = UIImageJPEGRepresentation(image, (CGFloat)1.0); } #else data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil]; #endif } [self storeImageDataToDisk:data forKey:key]; }); } }
從代碼能夠看出,先設置的內存緩存,再設置的磁盤緩存。ide
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion { if (key == nil) { return; } if (self.shouldCacheImagesInMemory) { [self.memCache removeObjectForKey:key]; } if (fromDisk) { dispatch_async(self.ioQueue, ^{ [_fileManager removeItemAtPath:[self defaultCachePathForKey:key] error:nil]; if (completion) { dispatch_async(dispatch_get_main_queue(), ^{ completion(); }); } }); } else if (completion){ completion(); } }
刪除緩存的時候也是先刪除內存緩存,在刪除磁盤緩存。oop
經過UIImageView
集成SDWebImage
異步下載和緩存遠程圖像。
下載圖片的核心方法以下:
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock { [self sd_cancelCurrentImageLoad]; objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); if (!(options & SDWebImageDelayPlaceholder)) { dispatch_main_async_safe(^{ //保證是在主線程中設置圖片 self.image = placeholder; }); } if (url) { // check if activityView is enabled or not if ([self showActivityIndicatorView]) { [self addActivityIndicator]; } __weak __typeof(self)wself = self; id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { [wself removeActivityIndicator]; if (!wself) return; dispatch_main_sync_safe(^{ if (!wself) return; if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) { completedBlock(image, error, cacheType, url); return; } else if (image) { wself.image = image; [wself setNeedsLayout]; } else { if ((options & SDWebImageDelayPlaceholder)) { wself.image = placeholder; [wself setNeedsLayout]; } } if (completedBlock && finished) { completedBlock(image, error, cacheType, url); } }); }]; [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"]; } else { dispatch_main_async_safe(^{ [self removeActivityIndicator]; if (completedBlock) { NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; completedBlock(nil, error, SDImageCacheTypeNone, url); } }); } }
從代碼能夠看出,代碼使用時runtime爲分類添加屬性,設置placeholder
必定是在住線程中進行的,圖片真正的下載操做,使用的是SDWebImageOperation
。
圖片加載的時序圖:
並且這裏有兩個值得咱們學習的宏定義:
#define dispatch_main_sync_safe(block)\ if ([NSThread isMainThread]) {\ block();\ } else {\ dispatch_sync(dispatch_get_main_queue(), block);\ }
保證同步線程是在主線程執行的。
#define dispatch_main_async_safe(block)\ if ([NSThread isMainThread]) {\ block();\ } else {\ dispatch_async(dispatch_get_main_queue(), block);\ }
保證異步線程是在主線程執行的。
SDWebImageManager是整個框架的一個核心類,它把SDImageCache
和SDWebImageDownloader
結合在一塊兒,同時管理圖片的下載和緩存操做。
SDWebImageOptions
這個options,提供了咱們下載圖片時的不少可選操做。
示例以下:
/** * By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying. * 默認狀況下,當一個 URL 下載失敗,該URL被列入黑名單,將不會繼續嘗試下載 * This flag disable this blacklisting. * 此標誌取消黑名單 */ SDWebImageRetryFailed = 1 << 0, /** * By default, image downloads are started during UI interactions, this flags disable this feature, * 默認狀況下,在 UI 交互時也會啓動圖像下載,此標記取消這一功能 * leading to delayed download on UIScrollView deceleration for instance. * 會延遲下載,UIScrollView中止滾動以後再繼續下載 * 下載事件監聽的運行循環模式是 NSDefaultRunLoopMode */ SDWebImageLowPriority = 1 << 1, /** * This flag disables on-disk caching * 禁用磁盤緩存 */ SDWebImageCacheMemoryOnly = 1 << 2,
注:圖片來源