AFN AFAutoPurgingImageCache 源碼分析

AFN AFAutoPurgingImageCache 源碼分析

其實(shí)這個(gè)源碼特別簡(jiǎn)單许溅,但是卻可以學(xué)習(xí)人家的思想汹忠,在我們平時(shí)開發(fā)中,使用 image 的頻率是非常非常大的,但如果我們經(jīng)常調(diào)用 imageNamed這個(gè)系統(tǒng) api 去加載本地圖片的時(shí)候乾蓬,是會(huì)耗時(shí)額河劝,imageNamed 主要做兩件事壁榕,第一 I/O 操作,首次加載圖片是需要去磁盤中的赎瞎,所以有 I/O 操作牌里,然后對(duì)圖片進(jìn)行解碼,將位圖數(shù)據(jù)還原為原始像素?cái)?shù)據(jù),這里加載圖片就先不說 SD 和 YY 加載圖片的技術(shù)了牡辽,這里 AFN 用的是一個(gè)內(nèi)存緩存的一個(gè)思路喳篇,在平時(shí)開發(fā)中也可以應(yīng)用

AFCachedImage

圖片的緩存類,相當(dāng)于映射系統(tǒng)的 UIImage 态辛, 但是比 UIImage 多了幾個(gè)屬性麸澜,如當(dāng)前圖片的大小,因?yàn)橐旁诰彺嬷凶嗪冢孕枰?jì)算當(dāng)前圖片占用的大小炊邦,然后是圖片的訪問時(shí)間,因?yàn)闀?huì)限定緩存圖片需要占用的總大小熟史,所以馁害,如果超出限制需要淘汰,就是根據(jù)這個(gè)訪問時(shí)間淘汰的蹂匹,這就是這個(gè)類的作用

AFAutoPurgingImageCache


- (instancetype)init {
    // 默認(rèn)緩存容量為100M碘菜,清除后保留60M容量
    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);

        // 發(fā)生內(nèi)存警告之后,清除所有圖片
        [[NSNotificationCenter defaultCenter]
         addObserver:self
         selector:@selector(removeAllImages)
         name:UIApplicationDidReceiveMemoryWarningNotification
         object:nil];

    }
    return self;
}

初始化緩存類限寞,主要初始化忍啸,緩存允許的總大小,如超出總大小之后履植,需要留下多少緩存计雌,比如我們?cè)试S緩存中總共存儲(chǔ)100M的圖片,如果超出一百兆静尼,我們清除40M白粉,留下60M。



- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier {
    dispatch_barrier_async(self.synchronizationQueue, ^{
        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];
            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;
        }
    });
}

添加圖片鼠渺,這里使用的是柵欄函數(shù)鸭巴,主要就是多讀單寫,這里添加圖片拦盹,相當(dāng)于寫鹃祖,單寫的,每次添加圖片額時(shí)候普舆,根據(jù)ID判斷緩存中是否有緩存恬口,如果有,那么就先將當(dāng)前總共占用額緩存減去已經(jīng)存在的ID對(duì)應(yīng)額那個(gè)圖片額緩存沼侣,然后將新的ID圖片添加到緩存祖能,然后當(dāng)前總緩存加上新的圖片額緩存,接下來蛾洛,判斷當(dāng)前的總緩存养铸,是不是超過我們預(yù)定號(hào)的總額memoryCapacity緩存雁芙,如果超過,就遍歷所有圖片钞螟,根據(jù)訪問時(shí)間進(jìn)行淘汰兔甘,直到剩余preferredMemoryUsageAfterPurge這么大為止,可以比這個(gè)值小鳞滨,那么就符合要求了

我們?cè)賮砜慈【彺娲笮?/p>

// 多讀單寫
- (UInt64)memoryUsage {
    __block UInt64 result = 0;
    dispatch_sync(self.synchronizationQueue, ^{
        result = self.currentMemoryUsage;
    });
    return result;
}

可以看到是同步多讀的洞焙,,利用柵欄函數(shù)實(shí)現(xiàn)多讀單寫


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


刪除也是個(gè)互斥單操作

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


從緩存中讀取圖片拯啦,是個(gè)同步多讀的操作澡匪,這就是柵欄函數(shù)的魅力。


- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier {
    [self addImage:image withIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]];
}

- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier {
    return [self removeImageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]];
}

- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)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;
}

這里主要是根據(jù) URL 來緩存圖片提岔,根據(jù)請(qǐng)求的url和一個(gè)ID仙蛉,來唯一確定一個(gè)ID笋敞,來緩存圖片

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末碱蒙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子夯巷,更是在濱河造成了極大的恐慌赛惩,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趁餐,死亡現(xiàn)場(chǎng)離奇詭異喷兼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)后雷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門季惯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人臀突,你說我怎么就攤上這事勉抓。” “怎么了候学?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵藕筋,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我梳码,道長(zhǎng)隐圾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任掰茶,我火速辦了婚禮暇藏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘濒蒋。我一直安慰自己盐碱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著甸各,像睡著了一般垛贤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上趣倾,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天聘惦,我揣著相機(jī)與錄音,去河邊找鬼儒恋。 笑死善绎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的诫尽。 我是一名探鬼主播禀酱,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼牧嫉!你這毒婦竟也來了剂跟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤酣藻,失蹤者是張志新(化名)和其女友劉穎曹洽,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辽剧,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡送淆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了怕轿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偷崩。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖撞羽,靈堂內(nèi)的尸體忽然破棺而出阐斜,到底是詐尸還是另有隱情,我是刑警寧澤放吩,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布智听,位于F島的核電站,受9級(jí)特大地震影響渡紫,放射性物質(zhì)發(fā)生泄漏到推。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一惕澎、第九天 我趴在偏房一處隱蔽的房頂上張望莉测。 院中可真熱鬧,春花似錦唧喉、人聲如沸捣卤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)董朝。三九已至鸠项,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間子姜,已是汗流浹背祟绊。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哥捕,地道東北人牧抽。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像遥赚,于是被迫代替她去往敵國(guó)和親扬舒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348