1、首先他是通過(guò)UIImageView的類別來(lái)設(shè)計(jì)的 UIImageView (WebCache)
在.m文件中最終都調(diào)用了這個(gè)方法進(jìn)行圖片網(wǎng)絡(luò)請(qǐng)求:
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
options:(SDWebImageOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageCompletionWithFinishedBlock)completedBlock
注:這個(gè)方法是在這個(gè)類中SDWebImageManager.sharedManager,通過(guò)這種方式,這個(gè)類應(yīng)該是單例類:
@implementation SDWebImageManager
+ (id)sharedManager {
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
}
這個(gè)方法足足寫(xiě)了140行代碼:下面一步一步分析;
a、首先會(huì)初始化一個(gè)隊(duì)列SDWebImageCombinedOperation,從緩存中獲取圖片代碼如下:
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock;
在上面方法體里判斷如果緩存里邊有圖片則通過(guò)block返回:
if (image && options & SDWebImageRefreshCached) {
dispatch_main_sync_safe(^{
// If image was found in the cache bug SDWebImageRefreshCached is provided, notify about the cached image
// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
completedBlock(image, nil, cacheType, YES, url);
});
}
b、否則就往下執(zhí)行,通過(guò)下面這個(gè)方法獲取網(wǎng)絡(luò)圖片
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock;
b1、下面就進(jìn)入上面的方法中分析如何實(shí)現(xiàn)網(wǎng)絡(luò)下載圖片:
在這個(gè)SDWebImageDownloader單例類中,又調(diào)用
SDWebImageDownloaderOperation類中的
- (id)initWithRequest:(NSURLRequest *)request
options:(SDWebImageDownloaderOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock
cancelled:(SDWebImageNoParamsBlock)cancelBlock
這個(gè)方法進(jìn)行網(wǎng)絡(luò)請(qǐng)求參數(shù)的初始化,由最后的NSURLConnection進(jìn)行網(wǎng)絡(luò)請(qǐng)求,最后在請(qǐng)求回來(lái)的block方法里邊把返回回來(lái)的image返回并且進(jìn)行緩存:
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
SDWebImageDownloader *sself = wself;
if (!sself) return;
__block NSArray *callbacksForURL;
dispatch_barrier_sync(sself.barrierQueue, ^{
callbacksForURL = [sself.URLCallbacks[url] copy];
if (finished) {
[sself.URLCallbacks removeObjectForKey:url];
}
});
for (NSDictionary *callbacks in callbacksForURL) {
SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
if (callback) callback(image, data, error, finished);
}
}
這個(gè)是緩存方法:
- (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
}
if (data) {
if (![_fileManager fileExistsAtPath:_diskCachePath]) {
[_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
}
// get cache Path for image key
NSString *cachePathForKey = [self defaultCachePathForKey:key];
// transform to NSUrl
NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
[_fileManager createFileAtPath:cachePathForKey contents:data attributes:nil];
// disable iCloud backup
if (self.shouldDisableiCloud) {
[fileURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil];
}
}
});
}
}
c、下面對(duì)上面緩存機(jī)制進(jìn)行分析:
它會(huì)根據(jù)jpg和png圖片,把不同的圖片格式轉(zhuǎn)化為NSData,然后再根據(jù)diskCachePath創(chuàng)建一個(gè)文件夾,最后把數(shù)據(jù)保存在沙盒里。
備注:這個(gè)我昨天寫(xiě)的實(shí)例不謀而合,雖然沒(méi)有人家這個(gè)容錯(cuò)和各方面考慮的周到,但是思想還是差不多的,明天會(huì)看一下,SDWebImage是如何實(shí)現(xiàn)gif圖片顯示的。順便在自己的組件上實(shí)現(xiàn)一下~~