AFNetworking 3.0 源碼解讀之 AFAutoPurgingImageCache

之前在UIImageView提到,在開啟圖片下載任務(wù)之前,會(huì)先查找緩存中是否有該圖片。

//5、如果圖片緩存存在秕重,就使用該緩存圖片 Use the image from the image cache if it exists
    UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil];

查找緩存圖片的這個(gè)方法- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier 來自AFAutoPurgingImageCache這個(gè)類。
在AFAutoPurgingImageCache的.h文件中申明了協(xié)議AFImageCache厉膀。

/**
 The `AFImageCache` protocol defines a set of APIs for adding, removing and fetching images from a cache synchronously.
 */

AFImageCache 這個(gè)協(xié)議有以下4個(gè)功能溶耘。


image.png

還有一個(gè)協(xié)議AFImageRequestCache,它繼承自AFImageCache服鹅,又?jǐn)U展了3個(gè)方法凳兵。


image.png

在AFAutoPurgingImageCache的.m文件中申明了AFCachedImage。

@interface AFCachedImage : NSObject

@property (nonatomic, strong) UIImage *image;
@property (nonatomic, strong) NSString *identifier; //圖片標(biāo)識(shí)符
@property (nonatomic, assign) UInt64 totalBytes;//圖片總大小
@property (nonatomic, strong) NSDate *lastAccessDate;//最近訪問時(shí)間
@property (nonatomic, assign) UInt64 currentMemoryUsage; // 當(dāng)前內(nèi)存容量

@end

而AFAutoPurgingImageCache的屬性中企软,有一個(gè)存放AFCachedImage對(duì)象的字典cachedImages庐扫。
注意一下,在AFNetworking中仗哨,緩存的圖片是放在NSMutableDictionary的可變字典中的形庭,不像SDWebImage那樣,有做2層緩存處理厌漂。

@interface AFAutoPurgingImageCache ()
@property (nonatomic, strong) NSMutableDictionary <NSString* , AFCachedImage*> *cachedImages;
@property (nonatomic, assign) UInt64 currentMemoryUsage;
@property (nonatomic, strong) dispatch_queue_t synchronizationQueue;
@end

了解了AFAutoPurgingImageCache的基本信息萨醒,下面倒著來看下這個(gè)緩存的方法。

- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier {
    return [self imageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]];
}

這個(gè)方法苇倡,又是通過圖片的identifier來尋找的富纸。
再看這個(gè)imageWithIdentifier方法

- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier {
    __block UIImage *image = nil;
    dispatch_sync(self.synchronizationQueue, ^{
        AFCachedImage *cachedImage = self.cachedImages[identifier];
        image = [cachedImage accessImage];
    });
    return image;
}

并行隊(duì)列 synchronizationQueue中,讀取圖片旨椒。

NSString *queueName = [NSString stringWithFormat:@"com.alamofire.autopurgingimagecache-%@", [[NSUUID UUID] UUIDString]];
        self.synchronizationQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);

此時(shí)晓褪,cachedImages這個(gè)字典派上用場(chǎng)了,通過identifier這個(gè)key综慎,尋找對(duì)應(yīng)的AFCachedImage類型的緩存圖片辞州。然后通過AFCachedImage類中的accessImage方法,先設(shè)置lastAccessDate最新訪問的時(shí)間為最新時(shí)間寥粹,再將屬性中存儲(chǔ)的image返回。

- (UIImage*)accessImage {
    self.lastAccessDate = [NSDate date];
    return self.image;
}

那這個(gè)self.image是怎么來的呢埃元?
再繼續(xù)往前找涝涤,AFCachedImage聲明了一個(gè)初始化的方法

@implementation AFCachedImage

-(instancetype)initWithImage:(UIImage *)image identifier:(NSString *)identifier {
    if (self = [self init]) {
        self.image = image;
        self.identifier = identifier;

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

這個(gè)方法里初始化了AFCachedImage的4個(gè)屬性,分別是圖片image岛杀,標(biāo)識(shí)符identifier阔拳,totalBytes總大小(字節(jié)為單位),lastAccessDate最新訪問時(shí)間(用于清理內(nèi)存時(shí)糊肠,進(jìn)行排序)辨宠。
這個(gè)初始化,在AFAutoPurgingImageCache類中货裹,添加圖片的方法里會(huì)用到嗤形。

- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier {
    //注意,這里使用了dispatch_barrier_async弧圆,相當(dāng)于把block( ^{})中的任務(wù)排在了這個(gè)并行隊(duì)列后面赋兵,
    dispatch_barrier_async(self.synchronizationQueue, ^{
   1、先是通過image和identifier初始化一個(gè)AFCachedImage
        AFCachedImage *cacheImage = [[AFCachedImage alloc] initWithImage:image identifier:identifier];
  2搔预、然后通過identifier找到cachedImages中保存的對(duì)應(yīng)對(duì)象previousCachedImage
        AFCachedImage *previousCachedImage = self.cachedImages[identifier];
3霹期、根據(jù)currentMemoryUsage是否存在重新計(jì)算currentMemoryUsage。
        if (previousCachedImage != nil) {
            self.currentMemoryUsage -= previousCachedImage.totalBytes;
        }

        self.cachedImages[identifier] = cacheImage;
        self.currentMemoryUsage += cacheImage.totalBytes;
    });
4拯田、另外一個(gè)dispatch_barrier_async中历造,通過currentMemoryUsage和memoryCapacity大小對(duì)比,將之前保存的緩存圖片數(shù)組根據(jù)lastAccessDate排序船庇。使用了LRU緩存策略吭产。
    dispatch_barrier_async(self.synchronizationQueue, ^{
        if (self.currentMemoryUsage > self.memoryCapacity) {
            UInt64 bytesToPurge = self.currentMemoryUsage - self.preferredMemoryUsageAfterPurge;
            NSMutableArray <AFCachedImage*> *sortedImages = [NSMutableArray arrayWithArray:self.cachedImages.allValues];
            NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastAccessDate"
                                                                           ascending:YES];
            [sortedImages sortUsingDescriptors:@[sortDescriptor]];

            UInt64 bytesPurged = 0;
5、將重新排序的數(shù)組溢十,遍歷數(shù)組垮刹,移除數(shù)據(jù)直到緩存容量小于等于限值
            for (AFCachedImage *cachedImage in sortedImages) {
                [self.cachedImages removeObjectForKey:cachedImage.identifier];
                bytesPurged += cachedImage.totalBytes;
                if (bytesPurged >= bytesToPurge) {
                    break ;
                }
            }
            self.currentMemoryUsage -= bytesPurged;
        }
    });
}

“這個(gè)方法是核心方法,在這個(gè)方法中张弛,一共做了兩件事:

把圖片加入到緩存字典中(注意字典中可能存在identifier的情況)荒典,然后計(jì)算當(dāng)前的容量大小
處理容量超過最大容量的異常情況。分為下邊幾個(gè)步驟: 1.比較容量是否超過最大容量 2.計(jì)算將要清楚的緩存容量 3.把所有緩存的圖片放到一個(gè)數(shù)組中 4.對(duì)這個(gè)數(shù)組按照最后訪問時(shí)間進(jìn)行排序吞鸭,優(yōu)先保留最后訪問的數(shù)據(jù) 5.遍歷數(shù)組寺董,移除圖片(當(dāng)已經(jīng)移除的數(shù)據(jù)大于應(yīng)該移除的數(shù)據(jù)時(shí)停止)”

參考文章:
AFNetworking 3.0 源碼解讀(七)之 AFAutoPurgingImageCache

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市刻剥,隨后出現(xiàn)的幾起案子遮咖,更是在濱河造成了極大的恐慌,老刑警劉巖造虏,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件御吞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡漓藕,警方通過查閱死者的電腦和手機(jī)陶珠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來享钞,“玉大人揍诽,你說我怎么就攤上這事。” “怎么了暑脆?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵渠啤,是天一觀的道長。 經(jīng)常有香客問我添吗,道長沥曹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任根资,我火速辦了婚禮架专,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘玄帕。我一直安慰自己部脚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布裤纹。 她就那樣靜靜地躺著委刘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鹰椒。 梳的紋絲不亂的頭發(fā)上锡移,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音漆际,去河邊找鬼淆珊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛奸汇,可吹牛的內(nèi)容都是我干的施符。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼擂找,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼戳吝!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起贯涎,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤听哭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后塘雳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陆盘,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年败明,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了礁遣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肩刃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情盈包,我是刑警寧澤沸呐,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站呢燥,受9級(jí)特大地震影響崭添,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叛氨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一呼渣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧寞埠,春花似錦屁置、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至饭冬,卻和暖如春使鹅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背昌抠。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工患朱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人炊苫。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓裁厅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親劝评。 傳聞我的和親對(duì)象是個(gè)殘疾皇子姐直,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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