SDWebImage源码解析

SDWebImage中核心类如下所示:

SDWebImage源码解析_第1张图片
26E06FD9-3B94-4AB4-9375-A2E7E5CC4470副本.png

一、SDImageCache
SDImageCache负责缓存图片,包含几个对外属性

//是否解压图片
@property (assign, nonatomic) BOOL shouldDecompressImages;
//iCloud 备份
@property (assign, nonatomic) BOOL shouldDisableiCloud;
//是否能够缓存图片到内存
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory;
//最大内存使用,内存
@property (assign, nonatomic) NSUInteger maxMemoryCost;
//缓存对象的数量最大值
@property (assign, nonatomic) NSUInteger maxMemoryCountLimit;
//缓存时间,默认一周
@property (assign, nonatomic) NSInteger maxCacheAge;
//缓存最大值,磁盘
@property (assign, nonatomic) NSUInteger maxCacheSize;

内部属性:

//缓存对象
@property (strong, nonatomic) NSCache *memCache;
//磁盘缓存路径
@property (strong, nonatomic) NSString *diskCachePath;
//自定义路径数组,如bundle里的图片,可以添加到该数组中
@property (strong, nonatomic) NSMutableArray *customPaths;
//读写队列
@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t ioQueue;

AutoPurgeCache,当收到内存警告时自动清空缓存

//初始化类的成员变量,监听内存警告、APP退到后台、APP终止通知

  • (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory
    //根据key值、path值生成文件路径
  • (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path
    //根据key值得到文件名
  • (NSString *)cachedFileNameForKey:(NSString )key
    //获取缓存到磁盘的路径
    -(NSString )makeDiskCachePath:(NSString)fullNamespace
    /
    存储图片到磁盘
    *recalculate表示是否需要把image类型转换为nsdata类型,如果不需要转换则把imageData直接写入磁盘,如果需要转换则把image转换为data
    */

  • (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];//把图片存储到缓存
    }

    if (toDisk) {
    dispatch_async(self.ioQueue, ^{
    NSData *data = imageData;
    // image非空才能转换为data
    if (image && (recalculate || !data)) {

if TARGET_OS_IPHONE

            //获取图片的alpha信息,判断是否是png图片
            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) {
                data = UIImagePNGRepresentation(image);
            }
            else {
                data = UIImageJPEGRepresentation(image, (CGFloat)1.0);
            }

else

            data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil];

endif

        }            
        // 把data写入磁盘
        if (data) {
            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:data attributes:nil];
            
            // disable iCloud backup
            if (self.shouldDisableiCloud) {
                [fileURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil];
            }
        }
    });
}

}

//查询图片是否已存在

  • (BOOL)diskImageExistsWithKey:(NSString *)key;
  • (void)diskImageExistsWithKey:(NSString *)key completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;

//从缓存中取图片

  • (UIImage *)imageFromMemoryCacheForKey:(NSString *)key
    //从磁盘中取图片,先从内存缓存中取,无则从磁盘中取
  • (UIImage *)imageFromDiskCacheForKey:(NSString *)key
    //根据key生成文件路径,先从默认图片路径中查找,无则从自定义路径中查找,返回image的data数据
  • (NSData *)diskImageDataBySearchingAllPathsForKey:(NSString *)key;
    //根据key找到image
  • (UIImage *)diskImageForKey:(NSString *)key;
    //根据key中后缀@2x、@3x,放大图片到2、3倍
  • (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image;

//在磁盘中查找图片

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

     //缓存中没有,则从硬盘查找,放在ioQueue中处理
    

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

//从缓存中移除图片,

  • (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion { if (key == nil) { return; } if (self.shouldCacheImagesInMemory) { [self.memCache removeObjectForKey:key]; } if (fromDisk) { dispatch_async(self.ioQueue, ^{ [_fileManager removeItemAtPath:[self defaultCachePathForKey:key] error:nil]; if (completion) { dispatch_async(dispatch_get_main_queue(), ^{ completion(); }); } }); } else if (completion){ completion();
    }
    }
    //从磁盘中移除缓存图片文件夹

  • (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion;
    //每次退到后台,清除已过期文件,并判断缓存文件夹是否大于设置的最大容量

  • (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock {
    dispatch_async(self.ioQueue, ^{
    //初始化缓存路径URL
    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;            }
    
          // 文件最后一次修改的日期与过期日期比较谁更晚,laterDate函数返回两个日期中较晚的一个时间,如果修改日期小于过期日期则删除该文件
          NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];            if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {                [urlsToDelete addObject:fileURL];                continue;            }
    
          // 统计未过期文件的大小
          NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];            currentCacheSize += [totalAllocatedSize unsignedIntegerValue];            [cacheFiles setObject:resourceValues forKey:fileURL];
      }
      // 删除已过期文件
      for (NSURL *fileURL in urlsToDelete) {                 
      [_fileManager removeItemAtURL:fileURL error:nil];          
      }
    
      // 如果缓存文件容量已经大于设置的容量则删除最早的文件
      if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) {
          // 最大缓存容量的一半
          const NSUInteger desiredCacheSize = self.maxCacheSize / 2;
    
          // 由修改日期对文件排序
          NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent      
                                      usingComparator:^NSComparisonResult(id obj1, id obj2) {                                                                
           return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];                                                            }];
    
          //删除文件直到文件夹的容量 < 最大容量的一半
          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(); }); } });
}

// 获取已缓存文件的总容量

  • (NSUInteger)getSize { __blockNSUInteger size = 0; dispatch_sync(self.ioQueue, ^{ NSDirectoryEnumerator *fileEnumerator = [_fileManagerenumeratorAtPath:self.diskCachePath]; for (NSString *fileName in fileEnumerator) { NSString *filePath = [self.diskCachePathstringByAppendingPathComponent:fileName]; NSDictionary *attrs = [[NSFileManagerdefaultManager] attributesOfItemAtPath:filePath error:nil]; size += [attrs fileSize]; } }); return size;}

// 获取已缓存图片的总数量

  • (NSUInteger)getDiskCount { __blockNSUInteger count = 0; dispatch_sync(self.ioQueue, ^{ NSDirectoryEnumerator *fileEnumerator = [_fileManagerenumeratorAtPath:self.diskCachePath]; count = [[fileEnumerator allObjects] count]; }); return count;
    }

// 计算文件大小及文件数量

  • (void)calculateSizeWithCompletionBlock:(SDWebImageCalculateSizeBlock)completionBlock { NSURL *diskCacheURL = [NSURLfileURLWithPath:self.diskCachePathisDirectory:YES]; dispatch_async(self.ioQueue, ^{ NSUInteger fileCount = 0; NSUInteger totalSize = 0; NSDirectoryEnumerator *fileEnumerator = [_fileManagerenumeratorAtURL:diskCacheURL includingPropertiesForKeys:@[NSFileSize] options:NSDirectoryEnumerationSkipsHiddenFiles errorHandler:NULL]; for (NSURL *fileURL in fileEnumerator) { NSNumber *fileSize; [fileURL getResourceValue:&fileSize forKey:NSURLFileSizeKeyerror:NULL]; totalSize += [fileSize unsignedIntegerValue]; fileCount += 1; } if (completionBlock) { dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(fileCount, totalSize); }); } });
    }

你可能感兴趣的:(SDWebImage源码解析)