源碼閱讀:AFNetworking(八)——AFAutoPurgingImageCache

該文章閱讀的AFNetworking的版本爲3.2.0。緩存

AFAutoPurgingImageCache該類是用來管理內存中圖片的緩存。bash

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協議,擴展了增刪查的方法async

/**
 詢問是否能以指定的請求和附加標識符來緩存圖像。
 */
- (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私有類

這個類是用來描述被緩存的圖片ide

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;
}
複製代碼

源碼閱讀系列:AFNetworkingpost

源碼閱讀:AFNetworking(一)——從使用入手ui

源碼閱讀:AFNetworking(二)——AFURLRequestSerializationatom

源碼閱讀:AFNetworking(三)——AFURLResponseSerializationspa

源碼閱讀:AFNetworking(四)——AFSecurityPolicy3d

源碼閱讀: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

相關文章
相關標籤/搜索