美文网首页
六、SDWebImage源码解读SDImageCache(二)

六、SDWebImage源码解读SDImageCache(二)

作者: 小强简书 | 来源:发表于2018-02-06 15:13 被阅读27次

上一篇我们主要介绍了下SDImageCache的方法的含义和实现,这篇主要介绍SDImageCache的重点方法。

一、这个方法在缓存中查询对应key的图片信息 包含image,diskData以及缓存的类型返回一个NSOperation

- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock {
    if (!key) { //若key == nil ,直接返回,判空
        if (doneBlock) {
            doneBlock(nil, nil, SDImageCacheTypeNone);
        }
        return nil;
    }

    // First check the in-memory cache... 第一步:从内存中取出image
    UIImage *image = [self imageFromMemoryCacheForKey:key]; //从缓存当中取出image
    if (image) {
        NSData *diskData = nil;
        if (image.images) {//如果是gif图像,这从disk中提取完整图像
            diskData = [self diskImageDataBySearchingAllPathsForKey:key];
        }
        if (doneBlock) {
            doneBlock(image, diskData, SDImageCacheTypeMemory);
        }
        return nil;
    }

    NSOperation *operation = [NSOperation new];
    dispatch_async(self.ioQueue, ^{ // 异步
        if (operation.isCancelled) { //如果当前operation 为caneclled 直接返回
            // do not call the completion if cancelled
            return;
        }

        @autoreleasepool {
            //到这里说明已经来到磁盘缓存区获取 然后回调结束
            NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
            UIImage *diskImage = [self diskImageForKey:key];
            if (diskImage && self.config.shouldCacheImagesInMemory) {//如果有值,并且为shouldCacheImagesInMemory的时候将image缓存起来
                NSUInteger cost = SDCacheCostForImage(diskImage); //获取图片字节数
                [self.memCache setObject:diskImage forKey:key cost:cost];
            }

            if (doneBlock) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    doneBlock(diskImage, diskData, SDImageCacheTypeDisk);
                });
            }
        }
    });

    return operation;
}

二、应用进入后台的时候,调用这个方法 然后清除过期图片

 [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(clearMemory)
                                                     name:UIApplicationDidReceiveMemoryWarningNotification
                                                   object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(deleteOldFiles)
                                                     name:UIApplicationWillTerminateNotification
                                                   object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(backgroundDeleteOldFiles)
                                                     name:UIApplicationDidEnterBackgroundNotification
                                                   object:nil];

- (void)backgroundDeleteOldFiles {
    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 deleteOldFilesWithCompletionBlock:^{
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
}

方法通过向iOS申请,在后台完成一个Long-Running Task任务,当一个 iOS 应用被送到后台,它的主线程会被暂停。你用 NSThread 的 detachNewThreadSelector:toTar get:withObject:类方法创建的线程也被挂起了。
如果你想在后台完成一个长期任务,就必须调用 UIApplication 的 beginBackgroundTaskWithExpirationHandler:实例方法,来向 iOS 借点时间。
默认情况下,如果在这个期限内,长期任务没有被完成,iOS 将终止程序。
怎么办?可以使用 beginBackgroundTaskWithExpirationHandler:实例方法,来向 iOS 再借点时间。经过证明,即使时执行Long-Running Task 任务,当程序被调到后台后,也是有时间限制的。一般为10分总(600s)

三、SDImageCacheConfig,缓存配置对象,包含所有配置项(解压缩图像,iCloud备份, 最长缓存时间,等等)我们自己在封装类的时候,应该学习这种方式

/**
 * Decompressing images that are downloaded and cached can improve performance but can consume lot of memory.
 * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption.
 * 解压缩图像下载和缓存可以提高性能,但会消耗大量的内存。
 * 默认为yes。如果由于内存消耗过多而发生崩溃,请将此设置为NO。
 */

@property (assign, nonatomic) BOOL shouldDecompressImages;

/**
 * disable iCloud backup [defaults to YES]
 是否禁用iCloud备份, 默认为YES
 */
@property (assign, nonatomic) BOOL shouldDisableiCloud;

/**
 * use memory cache [defaults to YES]
 是否缓存到内存中,默认为YES
 */
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory;

/**
 * The reading options while reading cache from disk.
 * Defaults to 0. You can set this to mapped file to improve performance.
  最大的缓存不过期时间, 单位为秒,默认为一周的时间
 */
@property (assign, nonatomic) NSDataReadingOptions diskCacheReadingOptions;

/**
 * The maximum length of time to keep an image in the cache, in seconds.
 最长缓存时间 默认为1周
 */
@property (assign, nonatomic) NSInteger maxCacheAge;

/**
 * The maximum size of the cache, in bytes.
 最大的缓存的size,单位是字节
 */
@property (assign, nonatomic) NSUInteger maxCacheSize;

四、NSCache

NSCache是系统提供的一种类似于集合(NSMutableDictionary)的缓存,它与集合的不同如下:

  1. NSCache具有自动删除的功能,以减少系统占用的内存;
  2. NSCache是线程安全的,不需要加线程锁;
  3. 键对象不会像 NSMutableDictionary 中那样被复制。(键不需要实现 NSCopying 协议)。

添加到缓存

- (nullable ObjectType)objectForKey:(KeyType)key;
- (void)setObject:(ObjectType)obj forKey:(KeyType)key; // 0 cost
- (void)setObject:(ObjectType)obj forKey:(KeyType)key cost:(NSUInteger)g;

删除缓存指定key

- (void)removeObjectForKey:(KeyType)key;

删除全部

- (void)removeAllObjects;

totalCostLimit:设置缓存占用的内存大小,并不是一个严格的限制,当总数超过了totalCostLimit设定的值,系统会清除一部分缓存,直至总消耗低于totalCostLimit的值

countLimit:设置缓存对象的大小,这也不是一个严格的限制。

代理方法

  • (void)cache:(NSCache *)cache willEvictObject:(id)obj;
    第一个参数是当前缓存(NSCache),不要修改该对象;
    第二个参数是当前将要被清理的对象,如果需要存储该对象,可以在此操作(存入Sqlite or CoreData);

五、计算图片大小(内联函数)

FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
#if SD_MAC
    return image.size.height * image.size.width;
#elif SD_UIKIT || SD_WATCH
    return image.size.height * image.size.width * image.scale * image.scale;
#endif
}

相关文章

网友评论

      本文标题:六、SDWebImage源码解读SDImageCache(二)

      本文链接:https://www.haomeiwen.com/subject/lspgzxtx.html