原始碼閱讀:AFNetworking(八)——AFAutoPurgingImageCache

堯少羽發表於2018-03-09

該文章閱讀的AFNetworking的版本為3.2.0。

AFAutoPurgingImageCache該類是用來管理記憶體中圖片的快取。

1.介面檔案

1.1.AFImageCache協議

這個協議定義了一些對快取中圖片增刪查的同步操作

/**
 以指定的識別符號向快取中新增圖片
 */
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier;

/**
 移除快取中指定識別符號的圖片
 */
- (BOOL)removeImageWithIdentifier:(NSString *)identifier;

/**
 移除快取中所有的圖片
 */
- (BOOL)removeAllImages;

/**
 獲取快取中指定識別符號的圖片
 */
- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier;
複製程式碼

1.2.AFImageRequestCache協議

這個協議繼承了AFImageCache協議,擴充套件了增刪查的方法

/**
 詢問是否能以指定的請求和附加識別符號來快取影象。
 */
- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;

/**
 以指定的請求和附加識別符號向快取中新增圖片
 */
- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;

/**
 移除快取中指定的請求和附加識別符號的圖片
 */
- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;

/**
 獲取快取中指定的請求和附加識別符號的圖片
 */
- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
複製程式碼

1.3.AFAutoPurgingImageCache介面

1.3.1.屬性

/**
 用來快取的總的記憶體容量
 */
@property (nonatomic, assign) UInt64 memoryCapacity;

/**
 清除快取時應當保留的快取容量
 */
@property (nonatomic, assign) UInt64 preferredMemoryUsageAfterPurge;

/**
 當前快取已經用掉的容量
 */
@property (nonatomic, assign, readonly) UInt64 memoryUsage;
複製程式碼

1.3.2.方法

/**
 初始化方法,預設總容量為100M,保留容量為60M
 */
- (instancetype)init;

/**
 初始化方法,自定義總容量和保留容量
 */
- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity;
複製程式碼

2.實現檔案

2.1.AFCachedImage私有類

這個類是用來描述被快取的圖片

2.1.1.介面

/**
 被快取的圖片
 */
@property (nonatomic, strong) UIImage *image;

/**
 被快取圖片的標識
 */
@property (nonatomic, strong) NSString *identifier;

/**
 被快取圖片的大小
 */
@property (nonatomic, assign) UInt64 totalBytes;

/**
 被快取圖片最後訪問的時間
 */
@property (nonatomic, strong) NSDate *lastAccessDate;

/**
 當前記憶體使用大小
 */
@property (nonatomic, assign) UInt64 currentMemoryUsage;
複製程式碼

2.1.2.實現

-(instancetype)initWithImage:(UIImage *)image identifier:(NSString *)identifier {
    if (self = [self init]) {
        // 屬性儲存引數
        self.image = image;
        self.identifier = identifier;

        // 獲取圖片的畫素尺寸,並以每畫素4位元組計算圖片大小
        CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale);
        CGFloat bytesPerPixel = 4.0;
        CGFloat bytesPerSize = imageSize.width * imageSize.height;
        self.totalBytes = (UInt64)bytesPerPixel * (UInt64)bytesPerSize;
        // 獲取當前時間儲存為最後訪問時間
        self.lastAccessDate = [NSDate date];
    }
    return self;
}

- (UIImage*)accessImage {
    // 記錄獲取被快取的圖片的時間
    self.lastAccessDate = [NSDate date];
    return self.image;
}

- (NSString *)description {
    // 定製列印資料
    NSString *descriptionString = [NSString stringWithFormat:@"Idenfitier: %@  lastAccessDate: %@ ", self.identifier, self.lastAccessDate];
    return descriptionString;

}
複製程式碼

2.2.類擴充套件

/**
 用可變字典儲存快取圖片
 */
@property (nonatomic, strong) NSMutableDictionary <NSString* , AFCachedImage*> *cachedImages;

/**
 當前記憶體使用量
 */
@property (nonatomic, assign) UInt64 currentMemoryUsage;

/**
 同步佇列
 */
@property (nonatomic, strong) dispatch_queue_t synchronizationQueue;
複製程式碼

2.3.方法實現

  • 生命週期方法
- (instancetype)init {
    // 呼叫下面的方法
    return [self initWithMemoryCapacity:100 * 1024 * 1024 preferredMemoryCapacity:60 * 1024 * 1024];
}

- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity {
    if (self = [super init]) {
        // 初始化屬性
        self.memoryCapacity = memoryCapacity;
        self.preferredMemoryUsageAfterPurge = preferredMemoryCapacity;
        self.cachedImages = [[NSMutableDictionary alloc] init];

        // 自定義併發佇列
        NSString *queueName = [NSString stringWithFormat:@"com.alamofire.autopurgingimagecache-%@", [[NSUUID UUID] UUIDString]];
        self.synchronizationQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);

        // 新增通知監聽記憶體警告
        [[NSNotificationCenter defaultCenter]
         addObserver:self
         selector:@selector(removeAllImages)
         name:UIApplicationDidReceiveMemoryWarningNotification
         object:nil];

    }
    return self;
}

- (void)dealloc {
    // 移除通知監聽
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
複製程式碼
  • 公共方法實現
- (UInt64)memoryUsage {
    // 同步併發佇列獲取當前記憶體使用量
    __block UInt64 result = 0;
    dispatch_sync(self.synchronizationQueue, ^{
        result = self.currentMemoryUsage;
    });
    return result;
}
複製程式碼
  • AFImageCache協議方法實現
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier {
    // 等待之前佇列中的任務完成後再執行以下程式碼
    dispatch_barrier_async(self.synchronizationQueue, ^{
        // 建立AFCachedImage物件
        AFCachedImage *cacheImage = [[AFCachedImage alloc] initWithImage:image identifier:identifier];

        // 檢查快取中是否已經有指定識別符號的快取圖片,如果有就刪除
        AFCachedImage *previousCachedImage = self.cachedImages[identifier];
        if (previousCachedImage != nil) {
            self.currentMemoryUsage -= previousCachedImage.totalBytes;
        }

        // 儲存圖片
        self.cachedImages[identifier] = cacheImage;
        // 重新計算快取
        self.currentMemoryUsage += cacheImage.totalBytes;
    });

    // 等待之前佇列中的任務完成後再執行以下程式碼
    dispatch_barrier_async(self.synchronizationQueue, ^{
        // 如果當前記憶體使用量已經超出了最大記憶體使用量
        if (self.currentMemoryUsage > self.memoryCapacity) {
            // 計算需要清除的快取量
            UInt64 bytesToPurge = self.currentMemoryUsage - self.preferredMemoryUsageAfterPurge;
            // 獲取到目前所有的圖片
            NSMutableArray <AFCachedImage*> *sortedImages = [NSMutableArray arrayWithArray:self.cachedImages.allValues];
            // 設定排序描述物件為按照屬性lastAccessDate的升序排列
            NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastAccessDate"
                                                                           ascending:YES];
            // 按照排序描述物件進行重排
            [sortedImages sortUsingDescriptors:@[sortDescriptor]];

            // 設定臨時變數儲存已清除快取的大小
            UInt64 bytesPurged = 0;

            // 遍歷已快取的圖片
            for (AFCachedImage *cachedImage in sortedImages) {
                // 從快取中刪除指定識別符號的圖片
                [self.cachedImages removeObjectForKey:cachedImage.identifier];
                // 計算已清除快取的大小
                bytesPurged += cachedImage.totalBytes;
                // 如果已清除快取量滿足了需要清除的快取量,就跳出迴圈不再清除
                if (bytesPurged >= bytesToPurge) {
                    break ;
                }
            }
            // 重新計算清除快取後的當前記憶體用量
            self.currentMemoryUsage -= bytesPurged;
        }
    });
}

- (BOOL)removeImageWithIdentifier:(NSString *)identifier {
    __block BOOL removed = NO;
    dispatch_barrier_sync(self.synchronizationQueue, ^{
        // 獲取到指定識別符號的圖片快取物件
        AFCachedImage *cachedImage = self.cachedImages[identifier];
        if (cachedImage != nil) {
            // 如果有這張圖片就從快取中刪除並重新計算當前記憶體使用量
            [self.cachedImages removeObjectForKey:identifier];
            self.currentMemoryUsage -= cachedImage.totalBytes;
            removed = YES;
        }
    });
    return removed;
}

- (BOOL)removeAllImages {
    __block BOOL removed = NO;
    dispatch_barrier_sync(self.synchronizationQueue, ^{
        if (self.cachedImages.count > 0) {
            // 刪除所有圖片快取並置零記憶體使用量
            [self.cachedImages removeAllObjects];
            self.currentMemoryUsage = 0;
            removed = YES;
        }
    });
    return removed;
}

- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier {
    __block UIImage *image = nil;
    dispatch_sync(self.synchronizationQueue, ^{
        // 獲取到指定識別符號的圖片快取物件
        AFCachedImage *cachedImage = self.cachedImages[identifier];
        image = [cachedImage accessImage];
    });
    return image;
}
複製程式碼
  • AFImageRequestCache協議方法實現
- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier {
    // 用request和identifier生成一個新識別符號後新增圖片
    [self addImage:image withIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]];
}

- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier {
    // 移除用request和identifier生成一個新識別符號的圖片
    return [self removeImageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]];
}

- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier {
    // 獲取用request和identifier生成一個新識別符號的圖片
    return [self imageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]];
}

- (NSString *)imageCacheKeyFromURLRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)additionalIdentifier {
    // 將識別符號拼在請求連結後面組成字串
    NSString *key = request.URL.absoluteString;
    if (additionalIdentifier != nil) {
        key = [key stringByAppendingString:additionalIdentifier];
    }
    return key;
}

- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier {
    // 只返回YES
    return YES;
}
複製程式碼

原始碼閱讀系列:AFNetworking

原始碼閱讀:AFNetworking(一)——從使用入手

原始碼閱讀:AFNetworking(二)——AFURLRequestSerialization

原始碼閱讀:AFNetworking(三)——AFURLResponseSerialization

原始碼閱讀:AFNetworking(四)——AFSecurityPolicy

原始碼閱讀:AFNetworking(五)——AFNetworkReachabilityManager

原始碼閱讀:AFNetworking(六)——AFURLSessionManager

原始碼閱讀:AFNetworking(七)——AFHTTPSessionManager

原始碼閱讀:AFNetworking(八)——AFAutoPurgingImageCache

原始碼閱讀:AFNetworking(九)——AFImageDownloader

原始碼閱讀:AFNetworking(十)——AFNetworkActivityIndicatorManager

原始碼閱讀:AFNetworking(十一)——UIActivityIndicatorView+AFNetworking

原始碼閱讀:AFNetworking(十二)——UIButton+AFNetworking

原始碼閱讀:AFNetworking(十三)——UIImageView+AFNetworking

原始碼閱讀:AFNetworking(十四)——UIProgressView+AFNetworking

原始碼閱讀:AFNetworking(十五)——UIRefreshControl+AFNetworking

原始碼閱讀:AFNetworking(十六)——UIWebView+AFNetworking

相關文章