iOS源碼解析—SDWebImage(SDImageCache)

概述

SDWebImage是iOS開發(fā)中加載圖片的庫,文件目錄的結(jié)構(gòu)按照功能可以分為4個部分:

數(shù)據(jù)緩存類:SDImageCache

數(shù)據(jù)下載類:SDWebImageDownloader、SDWebImageDownloaderOperation

工具類:SDWebImageManager、NSData+ImageContentType、SDWebImageCompat、UIImage+GIF等

UI的category:UIImageView+WebCache、UIButton+WebCache、UIImageView+HighlightedWebCache等。

本篇學(xué)習(xí)并分析一下緩存類SDImageCache。SDImageCache提供了內(nèi)存和硬盤兩種方式緩存圖片數(shù)據(jù),對于內(nèi)存緩存,直接將圖片對象UIImage存入即可,對于硬盤緩存,先將UIImage對象序列化為NSData字節(jié)流,寫入文件中。下面是SDImageCache的主要屬性:

@interface SDImageCache ()
@property (strong, nonatomic) NSCache *memCache; //內(nèi)存緩存
@property (strong, nonatomic) NSString *diskCachePath; //硬盤緩存
@property (strong, nonatomic) NSMutableArray *customPaths; //自定義硬盤緩存路徑
@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t ioQueue; //串行隊列,執(zhí)行存儲操作時的隊列
@end

其中NSCache對象memCache負責(zé)將UIImage對象存入內(nèi)存中,diskCachePath是存入圖片數(shù)據(jù)存入硬盤的路徑,customPaths是自定義的硬盤存儲路徑。ioQueue是串行隊列,由于存儲圖片數(shù)據(jù)是I/O操作,在ioQueue中執(zhí)行優(yōu)化可以提升主線程的流暢度。

初始化方法

  1. -(id)initWithNamespace:

    - (id)initWithNamespace:(NSString *)ns {
        NSString *path = [self makeDiskCachePath:ns]; //創(chuàng)建文件讀寫路徑
        return [self initWithNamespace:ns diskCacheDirectory:path]; //初始化參數(shù)
    }
    

    該方法首先調(diào)用makeDiskCachePath方法創(chuàng)建文件讀寫路徑,在文件系統(tǒng)的cache目錄中添加子目錄,然后調(diào)用initWithNamespace:diskCacheDirectory:方法初始化參數(shù)。

  2. -(id)initWithNamespace:diskCacheDirectory:

    - (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory {
        if ((self = [super init])) {
            NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns]; //文件緩存目錄名
            // PNG格式前綴
            kPNGSignatureData = [NSData dataWithBytes:kPNGSignatureBytes length:8];
            // 串行隊列
            _ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);
            // 最大緩存期限
            _maxCacheAge = kDefaultCacheMaxCacheAge;
            // NSCache對象,用于內(nèi)存緩存
            _memCache = [[AutoPurgeCache alloc] init];
            _memCache.name = fullNamespace;
    
            // 創(chuàng)建文件緩存目錄路徑
            if (directory != nil) {
                _diskCachePath = [directory stringByAppendingPathComponent:fullNamespace];
            } else {
                NSString *path = [self makeDiskCachePath:ns];
                _diskCachePath = path;
            }
            //需要解壓縮
            _shouldDecompressImages = YES;
            // 需要緩存到內(nèi)存
            _shouldCacheImagesInMemory = YES;
            // 禁用icloud
            _shouldDisableiCloud = YES;
            dispatch_sync(_ioQueue, ^{
                _fileManager = [NSFileManager new];
            });
          ...
        return self;
    }
    

    該方法初始化一系列參數(shù),主要是創(chuàng)建用于內(nèi)存緩存的對象memCache,文件緩存的路徑diskCachePath,設(shè)置標志位(需要解壓縮、需要緩存內(nèi)存、禁用iCloud)。初始化完成后,SDImageCache提供了存儲數(shù)據(jù)、查詢數(shù)據(jù)、刪除數(shù)據(jù)等功能。

存儲圖片數(shù)據(jù)

存儲數(shù)據(jù)調(diào)用的方法是-(void)storeImage:recalculateFromImage:imageData:forKey:toDisk:,代碼注釋如下:

- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk {
    if (!image || !key) {
        return;
    }
    if (self.shouldCacheImagesInMemory) {
        NSUInteger cost = SDCacheCostForImage(image);
        [self.memCache setObject:image forKey:key cost:cost]; //UIImage存入內(nèi)存
    }
    if (toDisk) {
        dispatch_async(self.ioQueue, ^{ //在ioQueue中執(zhí)行存儲邏輯
            NSData *data = imageData;
            if (image && (recalculate || !data)) {
                int alphaInfo = CGImageGetAlphaInfo(image.CGImage);
                BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
                                  alphaInfo == kCGImageAlphaNoneSkipFirst ||
                                  alphaInfo == kCGImageAlphaNoneSkipLast);
                BOOL imageIsPng = hasAlpha; //是否包含透明度信息

                // 是否包含透明度信息
                if ([imageData length] >= [kPNGSignatureData length]) {
                    imageIsPng = ImageDataHasPNGPreffix(imageData);
                }

                if (imageIsPng) { // 包含透明度信息,屬于PNG格式的圖片,image轉(zhuǎn)成NSData
                    data = UIImagePNGRepresentation(image);
                }
                else { // 不包含透明度信息,屬于JPEG格式的圖片,image轉(zhuǎn)成NSData
                    data = UIImageJPEGRepresentation(image, (CGFloat)1.0);
                }
            }
            [self storeImageDataToDisk:data forKey:key]; //存儲數(shù)據(jù)
        });
    }
}

首先根據(jù)shouldCacheImagesInMemory判斷是否先寫入內(nèi)存,如果為YES,則將UIImage寫入memCache中,然后在ioQueue隊列中將UIImage轉(zhuǎn)成NSData,首先判斷是否有透明度信息,通過ImageDataHasPNGPreffix方法可以判斷圖片是否是PNG格式,如果是PNG格式,則調(diào)用UIImagePNGRepresentation方法轉(zhuǎn)換,否則用UIImageJPEGRepresentation方法轉(zhuǎn)成NSData,最后調(diào)用storeImageDataToDisk:forKey:方法存儲數(shù)據(jù)。storeImageDataToDisk:forKey:方法的代碼注釋如下:

- (void)storeImageDataToDisk:(NSData *)imageData forKey:(NSString *)key {
    if (!imageData) {
        return;
    }
    //創(chuàng)建文件存儲目錄
    if (![_fileManager fileExistsAtPath:_diskCachePath]) {
        [_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
    }
    //寫入圖片數(shù)據(jù)的文件路徑
    NSString *cachePathForKey = [self defaultCachePathForKey:key];
    NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
    //寫入圖片數(shù)據(jù)imageData到文件路徑中
    [_fileManager createFileAtPath:cachePathForKey contents:imageData attributes:nil];
    //禁用iCloud
    if (self.shouldDisableiCloud) {
        [fileURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil];
    }
}

該方法負責(zé)寫入圖片數(shù)據(jù)到文件中,首先判存儲數(shù)據(jù)的目錄是否存在,如不存在,先創(chuàng)建目錄,然后獲取寫入圖片數(shù)據(jù)的文件路徑,調(diào)用createFileAtPath:contents:attributes:方法寫入圖片數(shù)據(jù)到文件中。最后調(diào)用setResourceValue:forKey:error:方法設(shè)置禁用iCloud備份。其中defaultCachePathForKey:方法的注釋如下:

- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path {
    NSString *filename = [self cachedFileNameForKey:key];
    return [path stringByAppendingPathComponent:filename];
}

- (NSString *)defaultCachePathForKey:(NSString *)key {
    return [self cachePathForKey:key inPath:self.diskCachePath];
}

- (NSString *)cachedFileNameForKey:(NSString *)key {
    const char *str = [key UTF8String];
    if (str == NULL) {
        str = "";
    }
    unsigned char r[CC_MD5_DIGEST_LENGTH];
    CC_MD5(str, (CC_LONG)strlen(str), r);
    NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@",
                          r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
                          r[11], r[12], r[13], r[14], r[15], [[key pathExtension] isEqualToString:@""] ? @"" : [NSString stringWithFormat:@".%@", [key pathExtension]]];

    return filename;
}

該方法調(diào)用cachePathForKey:inPath:方法首先得到緩存key的md5值,然后將md5值拼接在path后面返回,作為寫入圖片數(shù)據(jù)的文件路徑。

訪問圖片數(shù)據(jù)

訪問圖片數(shù)據(jù)的方法主要有以下幾個:

  1. -(UIImage *)imageFromMemoryCacheForKey:方法,代碼注釋如下:

    - (UIImage *)imageFromMemoryCacheForKey:(NSString *)key {
        return [self.memCache objectForKey:key]; //從內(nèi)存中取數(shù)據(jù)
    }
    

    該方法從內(nèi)存中取圖片對象。

  2. -(UIImage *)imageFromDiskCacheForKey:方法,代碼注釋如下:

    - (UIImage *)imageFromDiskCacheForKey:(NSString *)key {
        // 首先從內(nèi)存中取
        UIImage *image = [self imageFromMemoryCacheForKey:key];
        if (image) {
            return image;
        }
        // 如果內(nèi)存中沒有數(shù)據(jù),則從文件中取
        UIImage *diskImage = [self diskImageForKey:key];
        if (diskImage && self.shouldCacheImagesInMemory) {
            NSUInteger cost = SDCacheCostForImage(diskImage);
            [self.memCache setObject:diskImage forKey:key cost:cost]; //數(shù)據(jù)寫入內(nèi)存
        }
        return diskImage;
    }
    

    該方法首先從內(nèi)存中去圖片,如果有直接返回,如果沒有,則調(diào)用diskImageForKey:方法從文件中取,如果存在,則將圖片數(shù)據(jù)存入內(nèi)存中,最后返回。diskImageForKey:方法返回文件中寫入的圖片,代碼注釋如下:

    - (UIImage *)diskImageForKey:(NSString *)key {
        NSData *data = [self diskImageDataBySearchingAllPathsForKey:key]; //從文件中獲取NSData數(shù)據(jù)
        if (data) {
            UIImage *image = [UIImage sd_imageWithData:data]; //轉(zhuǎn)化為UIImage
            image = [self scaledImageForKey:key image:image]; //轉(zhuǎn)化UIImage
            if (self.shouldDecompressImages) {
                image = [UIImage decodedImageWithImage:image]; //將image轉(zhuǎn)化為位圖image
            }
            return image;
        }
        else {
            return nil;
        }
    }
    

    該方法首先調(diào)用diskImageDataBySearchingAllPathsForKey:方法從文件中獲取緩存數(shù)據(jù),然后調(diào)用sd_imageWithData:方法根據(jù)data創(chuàng)建UIImage對象,然后調(diào)用decodedImageWithImage:方法將image轉(zhuǎn)成位圖格式的image。該方法代碼注釋如下:

    + (UIImage *)decodedImageWithImage:(UIImage *)image {
        if (image == nil) {
            return nil;
        }
        
        @autoreleasepool{
            if (image.images != nil) {
                return image;
            }
            
            CGImageRef imageRef = image.CGImage;
            //如果有alpha信息,則不轉(zhuǎn)化,直接返回image
            CGImageAlphaInfo alpha = CGImageGetAlphaInfo(imageRef);
            BOOL anyAlpha = (alpha == kCGImageAlphaFirst ||
                             alpha == kCGImageAlphaLast ||
                             alpha == kCGImageAlphaPremultipliedFirst ||
                             alpha == kCGImageAlphaPremultipliedLast);
            if (anyAlpha) {
                return image;
            }
            //獲取圖像的相關(guān)參數(shù)
            CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(CGImageGetColorSpace(imageRef));
            CGColorSpaceRef colorspaceRef = CGImageGetColorSpace(imageRef);
            
            BOOL unsupportedColorSpace = (imageColorSpaceModel == kCGColorSpaceModelUnknown ||
                                          imageColorSpaceModel == kCGColorSpaceModelMonochrome ||
                                          imageColorSpaceModel == kCGColorSpaceModelCMYK ||
                                          imageColorSpaceModel == kCGColorSpaceModelIndexed);
            if (unsupportedColorSpace) {
                colorspaceRef = CGColorSpaceCreateDeviceRGB();
            }
            size_t width = CGImageGetWidth(imageRef);
            size_t height = CGImageGetHeight(imageRef);
            NSUInteger bytesPerPixel = 4;
            NSUInteger bytesPerRow = bytesPerPixel * width;
            NSUInteger bitsPerComponent = 8;
    
            //創(chuàng)建context
            CGContextRef context = CGBitmapContextCreate(NULL,
                                                         width,
                                                         height,
                                                         bitsPerComponent,
                                                         bytesPerRow,
                                                         colorspaceRef,
                          kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);
            
            // 畫圖像
            CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
            //獲取位圖圖像
            CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context);
             //創(chuàng)建UIImage對象
            UIImage *imageWithoutAlpha = [UIImage imageWithCGImage:imageRefWithoutAlpha scale:image.scale                                            orientation:image.imageOrientation];
            
            if (unsupportedColorSpace) {
                CGColorSpaceRelease(colorspaceRef);
            }
            CGContextRelease(context);
            CGImageRelease(imageRefWithoutAlpha);
            return imageWithoutAlpha;
        }
    }
    

    該方法首先判斷圖片是否包含alpha信息,如果包含則不轉(zhuǎn)化直接返回。如果不包含則獲取相關(guān)參數(shù)創(chuàng)建上下文context對象,然后繪制位圖,并調(diào)用CGBitmapContextCreateImage方法取出位圖對象,最后調(diào)用imageWithCGImage:scale:orientation:方法生成UIImage對象并返回。imageRefWithoutAlpha是位圖,寬和高都是圖像的真是像素值,而imageWithoutAlpha對象則包含scale和imageOrientation信息,寬和高是根據(jù)scale比例縮放的寬和高。例如一張@2x的圖片,像素寬和高是512和256,在retina屏幕上的imageWithoutAlpha對象的寬和高是256和128,scale是2。imageRefWithoutAlpha的寬和高是512和256。

    之所以需要調(diào)用decodedImageWithImage:方法的原因是,通常APP加載的圖片源是PNG或者JPEG格式,當(dāng)調(diào)用圖片控件UIImageView加載圖片的時候,首先需要將其轉(zhuǎn)成位圖格式,然后渲染在屏幕上。預(yù)先在子線程中進行轉(zhuǎn)化,可以使UIImageView直接加載位圖,提升圖片加載的性能。

  3. -(NSOperation *)queryDiskCacheForKey: done: 方法

    該方法通過block的方式異步返回緩存的圖片,首先從內(nèi)存中獲取圖片,如果有則直接回調(diào)給上層。如果沒有,再從文件中獲取圖片,如能獲取到,則寫入內(nèi)存,同時將獲取到圖片回調(diào)給上層。代碼注釋如下:

    - (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {
        if (!doneBlock) {
            return nil;
        }
        if (!key) {
            doneBlock(nil, SDImageCacheTypeNone);
            return nil;
        }
        // 從內(nèi)存中獲取圖片
        UIImage *image = [self imageFromMemoryCacheForKey:key];
        if (image) {
            doneBlock(image, SDImageCacheTypeMemory); //能夠取到,直接回調(diào)給上層
            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); //寫入內(nèi)存
                    [self.memCache setObject:diskImage forKey:key cost:cost];
                }
                dispatch_async(dispatch_get_main_queue(), ^{
                    doneBlock(diskImage, SDImageCacheTypeDisk); //將圖片回調(diào)給上層
                });
            }
        });
        return operation;
    }
    

刪除圖片緩存數(shù)據(jù)

  1. -(void)removeImageForKey:fromDisk:withCompletion:方法

    該方法刪除key對應(yīng)的緩存數(shù)據(jù),分別從內(nèi)存和文件中刪除,代碼注釋如下:

    - (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion {
        if (key == nil) {
            return;
        }
        if (self.shouldCacheImagesInMemory) {
            [self.memCache removeObjectForKey:key]; //從內(nèi)存中刪除圖片數(shù)據(jù)
        }
        if (fromDisk) {
            dispatch_async(self.ioQueue, ^{
                [_fileManager removeItemAtPath:[self defaultCachePathForKey:key] error:nil]; //從文件中刪除圖片數(shù)據(jù)
                if (completion) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        completion();
                    });
                }
            });
        } else if (completion){
            completion();
        }
    }
    
  2. -(void)clearDiskOnCompletion:方法

    該方法通過刪除目錄的方式刪除文件中所有緩存的數(shù)據(jù),并重新緩存目錄,代碼注釋如下:

    - (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion
    {
        dispatch_async(self.ioQueue, ^{
            [_fileManager removeItemAtPath:self.diskCachePath error:nil]; //刪除存放圖片數(shù)據(jù)的目錄
            [_fileManager createDirectoryAtPath:self.diskCachePath
                    withIntermediateDirectories:YES
                                     attributes:nil
                                          error:NULL]; //重新創(chuàng)建目錄
            if (completion) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    completion();
                });
            }
        });
    }
    
  3. -(void)cleanDiskWithCompletionBlock:

    該方法根據(jù)緩存期限和緩存容量,刪除文件中的部分圖片數(shù)據(jù)。代碼注釋如下:

    - (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock {
        dispatch_async(self.ioQueue, ^{
            NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
            NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
    
            NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL includingPropertiesForKeys:resourceKeys                                                                    options:NSDirectoryEnumerationSkipsHiddenFiles errorHandler:NULL];
         //緩存期限
            NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];
            NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary];
            NSUInteger currentCacheSize = 0;
           
            NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init];
            for (NSURL *fileURL in fileEnumerator) {
                NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL];
             //如果是目錄,忽略
                if ([resourceValues[NSURLIsDirectoryKey] boolValue]) {
                    continue;
                }
             //將過期的緩存數(shù)據(jù)加入urlsToDelete數(shù)組中
                NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
                if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
                    [urlsToDelete addObject:fileURL];
                    continue;
                }
             //計算沒有過期的緩存數(shù)據(jù)的大小
                NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
                currentCacheSize += [totalAllocatedSize unsignedIntegerValue];
                [cacheFiles setObject:resourceValues forKey:fileURL];
            }
            //刪除過期的緩存數(shù)據(jù)
            for (NSURL *fileURL in urlsToDelete) {
                [_fileManager removeItemAtURL:fileURL error:nil];
            }
    
            //判斷沒有過期的緩存數(shù)據(jù)大小是否大于最大緩存容量
            if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) {
                //如果大于,則設(shè)置緩存最大容量的1/2為預(yù)留空間大小desiredCacheSize
                const NSUInteger desiredCacheSize = self.maxCacheSize / 2;
    
                //根據(jù)修改日期排序
                NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent                                                           usingComparator:^NSComparisonResult(id obj1, id obj2) {
                  return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];}];
    
                // 刪除緩存數(shù)據(jù),直到緩存數(shù)據(jù)總大小小于desiredCacheSize
                for (NSURL *fileURL in sortedFiles) {
                    if ([_fileManager removeItemAtURL:fileURL error:nil]) {
                        NSDictionary *resourceValues = cacheFiles[fileURL];
                        NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
                        currentCacheSize -= [totalAllocatedSize unsignedIntegerValue];
                        if (currentCacheSize < desiredCacheSize) {
                            break;
                        }
                    }
                }
            }
            if (completionBlock) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    completionBlock();
                });
            }
        });
    }
    

    一、該方法遍歷文件目錄下的所有緩存圖片數(shù)據(jù),將緩存日期超過最大期限expirationDate的數(shù)據(jù)刪除,同時計算剩余數(shù)據(jù)的總大小。

    二、判斷剩余圖片數(shù)據(jù)的總大小是否大于最大容量maxCacheSize,如果超過,則保留maxCacheSize的1/2空間desiredCacheSize,其余數(shù)據(jù)刪除,具體做法是對剩余圖片數(shù)據(jù)按照修改日期排序,逐個刪除,直到總大小小于desiredCacheSize為止。

其他方法

SDWebImage還提供了一些工具方法例如:

  1. -(NSUInteger)getSize方法,用于同步獲取緩存在文件中圖片數(shù)據(jù)總大小。

  2. -(NSUInteger)getDiskCount方法,用于同步獲取緩存在文件中圖片數(shù)據(jù)的個數(shù)。

  3. -(void)calculateSizeWithCompletionBlock:方法,用于異步獲取緩存在文件中圖片數(shù)據(jù)總大小。

  4. -(void)backgroundCleanDisk方法,用于在程序進入后臺的時候刪除超過期限和總大小的圖片數(shù)據(jù)。代碼注釋如下:

    - (void)backgroundCleanDisk {
        Class UIApplicationClass = NSClassFromString(@"UIApplication");
        if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
            return;
        }
        UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
        __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
            [application endBackgroundTask:bgTask]; //如果超時,則終止task
            bgTask = UIBackgroundTaskInvalid;
        }];
        //根據(jù)緩存期限和緩存容量,刪除文件中的部分圖片數(shù)據(jù)
        [self cleanDiskWithCompletionBlock:^{
            [application endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }];
    }
    

    該方法調(diào)用系統(tǒng)方法UIApplication的beginBackgroundTaskWithExpirationHandler方法開啟一個用于后臺執(zhí)行任務(wù)的task,并且設(shè)置超時的block。該方法允許在程序進入后臺的時候提供一段時間用于執(zhí)行app未完成的任務(wù),如果超出這段時間,則執(zhí)行ExpirationHandler的block,調(diào)用endBackgroundTask:方法終止task。cleanDiskWithCompletionBlock方法是在后臺執(zhí)行的代碼,執(zhí)行完成后會調(diào)用endBackgroundTask:方法終止task。

總結(jié)

SDImageCache同時使用內(nèi)存和文件來緩存數(shù)據(jù),設(shè)計思路值得學(xué)習(xí)和借鑒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內(nèi)容