您的位置:首页 > 编程语言

SDWebImage源代码阅读(三)

2016-09-18 16:45 92 查看

SDWebImage源代码阅读(三)

SDWebImage源代码阅读(一)——SDWebImage的使用和UIImageView+WebCache

SDWebImage源代码阅读(二)——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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: