SDWebImage源代码阅读(三)
2016-09-18 16:45
92 查看
SDWebImage源代码阅读(三)
SDWebImage源代码阅读(一)——SDWebImage的使用和UIImageView+WebCacheSDWebImage源代码阅读(二)——SDWebImageManager
SDImageCache
+ (SDImageCache *)sharedImageCache { static dispatch_once_t once; static id instance; dispatch_once(&once, ^{ instance = [self new]; }); return instance; }
SDImageCache是一个单例
初始化
- (id)init { return [self initWithNamespace:@"default"]; }
- (id)initWithNamespace:(NSString *)ns { NSString *path = [self makeDiskCachePath:ns]; return [self initWithNamespace:ns diskCacheDirectory:path]; }
- (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory { if ((self = [super init])) { NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns]; // initialise PNG signature data kPNGSignatureData = [NSData dataWithBytes:kPNGSignatureBytes length:8]; // Create IO serial queue _ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL); // Init default values _maxCacheAge = kDefaultCacheMaxCacheAge; // Init the memory cache _memCache = [[AutoPurgeCache alloc] init]; _memCache.name = fullNamespace; // Init the disk cache if (directory != nil) { _diskCachePath = [directory stringByAppendingPathComponent:fullNamespace]; } else { NSString *path = [self makeDiskCachePath:ns]; _diskCachePath = path; } // Set decompression to YES _shouldDecompressImages = YES; // memory cache enabled _shouldCacheImagesInMemory = YES; // Disable iCloud _shouldDisableiCloud = YES; dispatch_sync(_ioQueue, ^{ _fileManager = [NSFileManager new]; }); #if TARGET_OS_IOS // Subscribe to app events [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(clearMemory) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cleanDisk) name:UIApplicationWillTerminateNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundCleanDisk) name:UIApplicationDidEnterBackgroundNotification object:nil]; #endif } return self; }
使用第一种初始化方法,只能使用默认名称的缓存空间 (Library/Caches/default/com.hackemist.SDWebImageCache.default)
使用第二种初始化方法,可以使用其他命名
使用第三种初始化方法,可以使用其他命名和其他路径
UIApplicationDidReceiveMemoryWarningNotification会在系统内存低得时候发出警告,执行
(void)clearMemory { [self.memCache removeAllObjects]; }
UIApplicationWillTerminateNotification会在程序被杀死的时候,释放一定磁盘空间,执行
- (void)cleanDisk { [self cleanDiskWithCompletionBlock:nil]; }
kPNGSignatureData是PNG图片的信号data,方面之后的空判断
_maxCacheAge缓存最大天数,默认7天清理
UIApplicationDidEnterBackgroundNotification会在程序进入后台运行时,执行后台清理
- (void)backgroundCleanDisk { Class UIApplicationClass = NSClassFromString(@"UIApplication"); if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) { return; } UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)]; __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{ // Clean up any unfinished task business by marking where you // stopped or ending the task outright. [application endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; // Start the long-running task and return immediately. [self cleanDiskWithCompletionBlock:^{ [application endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; }
保存图片
- (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 = SDCacheC 4000 ostForImage(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]; }); } }
保存到内存
对image和key值进行判断,正确的时候调用
NSCache的方法
- (void)setObject:(ObjectType)obj forKey:(KeyType)key cost:(NSUInteger)g;
保存到磁盘
首先是异步队列执行,只有在图片存在并且磁盘里面没有这个缓存(或者是需要重新保存)才回去调用磁盘保存,经过一系列对图片的操作,执行磁盘保存操作
磁盘保存操作
- (void)storeImageDataToDisk:(NSData *)imageData forKey:(NSString *)key { if (!imageData) { return; } 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:imageData attributes:nil]; // disable iCloud backup if (self.shouldDisableiCloud) { [fileURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil]; } }
使用初始化
SDCache时创建的
NSFileManager的方法
- (BOOL)createFileAtPath:(NSString *)path contents:(nullable NSData *)data attributes:(nullable NSDictionary<NSString *, id> *)attr;去保存图片
取出图片
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock { if (!doneBlock) { return nil; } if (!key) { doneBlock(nil, SDImageCacheTypeNone); return nil; } // First check the in-memory cache... UIImage *image = [self imageFromMemoryCacheForKey:key]; if (image) { doneBlock(image, SDImageCacheTypeMemory); return nil; } NSOperation *operation = [NSOperation new]; dispatch_async(self.ioQueue, ^{ if (operation.isCancelled) { return; } @autoreleasepool { 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; }
当doneBlock为nil的时候,不进行后面操作,毕竟,你连取出图片后要干什么都不说,那取出来也只是浪费内存
当key为nil的时候,同样不进行后面操作,没有key,哪里来的图片
首先从内存中取出图片,如果取出来了,对doneBlock进行赋值,不进行之后的磁盘取图片缓存操作
如果内存中没有图片,就应该从磁盘中取出图片了,初始化操作,异步进行读取
- (UIImage *)diskImageForKey:(NSString *)key { NSData *data = [self diskImageDataBySearchingAllPathsForKey:key]; if (data) { UIImage *image = [UIImage sd_imageWithData:data]; image = [self scaledImageForKey:key image:image]; if (self.shouldDecompressImages) { image = [UIImage decodedImageWithImage:image]; } return image; } else { return nil; } }
读取到图片后,如果图片需要被保存到内存中,那么还要进行保存操作,之后对doneBlock进行赋值,返回操作
简而言之:SDImageCache,单例,缓存类,在内存和磁盘上缓存图片,并对图片编码,对图片进行保存和获取操作。
@Blog相关文章推荐
- SDWebImage源代码阅读(一)
- SDWebImage源代码阅读(二)
- SDWebImage源代码阅读(四)
- 如何阅读他人的Silverlight源代码
- 阅读作业之二
- 学会聪明的阅读
- 阅读内核源码 VIM+Ctags
- 构建之法阅读笔记02
- tinyhttp源码阅读(注释)
- 阅读离线版msdn
- [双语阅读]新西兰航空员工全裸出境 示范安全步骤
- Preftest性能测试工作室推荐您阅读《Java Performance Tuning》
- 金软有声阅读
- windos系统快捷键 2015-05-08 23:31 24人阅读 评论(0) 收藏
- 安卓学习过程、怎样阅读 Android 系统源码
- redis3.0.7源码阅读(二)源码文件归类
- 个性化阅读中的推荐系统(一)
- [双语阅读]全球最贵城市大洗牌 东京第一、北京第九
- 夹缝时代,书的阅读与未来
- 阅读C++编程思想时记录的小知识点