[iOS]SDWebImage 源码阅读(二)缓存

缓存部分从这部分开始

    operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {

我们进到里面看看SDWebImage的缓存是怎么做的。

    if (!doneBlock) {
        return nil;
    }

    if (!key) {
        doneBlock(nil, SDImageCacheTypeNone);
        return nil;
    }

首先是一些参数的判断,必须都要存在。

    UIImage *image = [self imageFromMemoryCacheForKey:key];
    if (image) {
        doneBlock(image, SDImageCacheTypeMemory);
        return nil;
    }

这几句代码是先从内存中根据key(url)取图片。

- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key {
    return [self.memCache objectForKey:key];
}

再看memCache的类型

@property (strong, nonatomic) NSCache *memCache;

会发现它内存缓存用的是NSCache,可能一般做一些缓存大家就直接用字典了,其实这样是不对的,首先没有NSCache那么便于管理,清理内存也不方便。

另外,可能大家没有注意到,memCache并不是普通的NSCache,而是作者继承NSCache而实用的AutoPurgeCache.

为什么要这么做呢?

http://stackoverflow.com/questions/19546054/nscache-crashing-when-memory-limit-is-reached-only-on-ios-7/19549090#19549090

看一下这个就知道了,在iOS7以后NSCache不能在内存警告的时候自动清除内存,经过我的测试确实是这样的,所以作者在这里继承了一个NScache,在里面加入了这样一个通知

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object: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);
            });
        }
    });

首先看这个ioQueue,他在创建的时候是一个串行队列,这样首先就是线程安全的了,然后由于io过程比较耗时,放在线程中也不会造成阻塞。

再看@autoreleasepool{};

他能够保证在区域结束时变量能够马上被释放,防止从硬盘将图片取出之后造成内存过高。

下面看从硬盘取出图片的过程。

- (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;
    }
}

首先取出图片的NSData数据,然后根据图片的格式做相应的操作,比如gif动图,会取出图片数组,然后用

[images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];

来返回可以动的图片。除此之外,还有webp类型喝普通类型。

然后还会根据图片的方向做一定的翻转。并且对图片进行解压(主要是去掉Alpha通道,不然系统会自动做这个操作,影响效率)。

然后会计算出最后图片的大小并放入内存中。


以上是取图片的过程,下面看存图片的过程。

从网络取回图片后,会掉用下面的方法,之前还有一个代理方法,可以不实现。

- (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];
        });
    }
}

首先判断是否需要放入内存,如果需要,就放到内存中。

然后判断是否需要放到硬盘中,如果需要:

又把这个过程放入异步串行队列中,并且根据图片的类型把图片变成二进制数据存入硬盘中。


后有有一个地方,可以禁止icloud同步图片。

    // disable iCloud backup
    if (self.shouldDisableiCloud) {
        [fileURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil];
    }


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值