SDWebImage源碼學習篇(一)

SDImageCache

  1. [UIScreen mainScreen].scale

    開始也以為是屏幕縮放,其實是判斷屏幕分辨率的方法玫坛。
    其值為1结笨、2、3時湿镀,分別對應@1x禀梳、@2x、@3x的圖片肠骆。

  2. __nullable__nonnull

    這兩個關(guān)鍵字之前就稍有接觸算途。是蘋果為了兼容OC與Swift混編時加入
    的新特性。以區(qū)別是否Swift中的Option蚀腿。具體戳這里:Objective-C新特性__nonnull和__nullable

  3. SDImageCache單例中維護著一個緩存集合NSCache嘴瓤,用以管理從Disk加載到內(nèi)存的圖片緩存。
    簡單介紹請看掘金的NSCache

    1.NSCache莉钙,與NSMutableDictionary的用法類似廓脆,但它是線程安全的,不需要加線程鎖磁玉。
    2.NSCache具有自動刪除的功能停忿,以減少系統(tǒng)占用的內(nèi)存。
    3.其對象不會被復制蚊伞,鍵不需要實現(xiàn) NSCopying 協(xié)議席赂。
    

    當收到內(nèi)存警告通知UIApplicationDidReceiveMemoryWarningNotification時吮铭,
    NSCache清理所有對象。

    當收到通知UIApplicationWillTerminateNotification程序被殺死時颅停,清理沙盒下的緩 存谓晌。

    當進入后臺時UIApplicationDidEnterBackgroundNotification,使用 UIBackgroundTaskIdentifier在后臺再運行一段時間癞揉,來處理過期的緩存圖片纸肉。


  4. 通過比較圖片Data形式十六進制,前8位Bytes喊熟,判斷圖片為PNG/JPG柏肪。SDWebImage中實現(xiàn)的方 法為BOOL ImageDataHasPNGPreffix(NSData *data);

    // PNG signature bytes and data (below)
    static unsigned char kPNGSignatureBytes[8] = 
    {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
    
  5. 創(chuàng)建一個對象ioQueue名為"com.hackemist.SDWebImageCache"的線程隊列,串行隊列按照FIFO順序執(zhí)行芥牌。一些NSFileManager有關(guān)的操作在這個線程中進行烦味。

  6. SDWebImage的圖片在緩存中的默認時間是一個星期。 可自行設置maxCacheAge

    static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; 
    // 1 week
    
  7. 計算圖片的字節(jié)數(shù)大小胳泉,并設置給NSCache的緩存中拐叉。存儲圖片到Memery的同時會重新轉(zhuǎn)化成Data(判斷JPG或PNG格式),然后使用FileManager存到相應的Disk路徑中扇商。值得注意一下的是SDCacheCostForImage

    if (self.shouldCacheImagesInMemory) {
        NSUInteger cost = SDCacheCostForImage(image);
        [self.memCache setObject:image forKey:key cost:cost];
    }
    
  8. 查詢圖片是否存在于硬盤Disk中凤瘦,Block返回存在的標志,在ioQueue線程中操作案铺。

  - (void)diskImageExistsWithKey:(NSString *)key 
  completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;
  1. SDImageCache查詢圖片的邏輯是先從NSCache中查詢蔬芥,如果有則返回圖片Image,沒有則從Disk中查詢(查詢目錄包括自定義的目錄)控汉,如果有就返回Image并把Image加載到Memory中笔诵。即第7點中所講。

    返回Image的過程中還進行了圖片的處理姑子,還原其分辨率格式(@2x乎婿、@3x、gif街佑、webp)谢翎、以及減壓縮decodedImageWithImage,而這個提示會有內(nèi)存暴增警告需要注意沐旨。

  2. 以下方法理解起來有點小迷糊:

```
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock 

//以下為方法中的部分實現(xiàn)
NSOperation *operation = [NSOperation new];
dispatch_async(self.ioQueue, ^{
    if (operation.isCancelled) {
        return;
    }

    @autoreleasepool {
        //Disk中查詢Image

        dispatch_async(dispatch_get_main_queue(), ^{
            doneBlock(diskImage, SDImageCacheTypeDisk);
        });
    }
});

```
它返回的是個NSOperation操作森逮,其實是查詢Image的存在方式(緩存中 Or Disk Or Nil),從而策略性的決定是否Image需要在網(wǎng)絡下載磁携。網(wǎng)上說的意思是`從磁盤或者內(nèi)存查詢的過程是異步的褒侧,后面可能需要cancel,所以這樣做`
  1. SDImageCache提供了兩個方法clearDiskcleanDisk,分別是清空整個圖片緩存目錄闷供、清空過期的緩存圖片烟央。
    - (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock {
    dispatch_async(self.ioQueue, ^{
        NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
        
        //設置要獲取的文件的信息:是否為文件、最后修改日期这吻、全部文件所占的大小
        NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];

        // This enumerator prefetches useful properties for our cache files.
        //生成一個目錄文件枚舉器吊档,忽略隱藏的文件
        NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
                                                   includingPropertiesForKeys:resourceKeys
                                                                      options:NSDirectoryEnumerationSkipsHiddenFiles
                                                                 errorHandler:NULL];
        //清算的日期
        NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];
        NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary];
        NSUInteger currentCacheSize = 0;

        // Enumerate all of the files in the cache directory.  This loop has two purposes:
        //
        //  1. Removing files that are older than the expiration date.
        //  2. Storing file attributes for the size-based cleanup pass.
        NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init];
        for (NSURL *fileURL in fileEnumerator) {
            NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL];

            // Skip directories.
            // 如果是目錄就跳過篙议,文件的就操作
            if ([resourceValues[NSURLIsDirectoryKey] boolValue]) {
                continue;
            }

            // Remove files that are older than the expiration date;
            // 比較日期唾糯,看緩存圖片是否需要清理
            NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
            if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
                [urlsToDelete addObject:fileURL];
                continue;
            }

            // Store a reference to this file and account for its total size.
            NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
            currentCacheSize += [totalAllocatedSize unsignedIntegerValue];
            
            //文件信息保存起來。
            //之后如果用戶設置的緩存大小,要比當前的計算出的緩存大小比要大的話鬼贱,
            //取出cacheFiles用來繼續(xù)比較時間移怯,刪除最之前的圖片緩存以滿足所設置的緩存需求
            [cacheFiles setObject:resourceValues forKey:fileURL];
        }
        
        
        for (NSURL *fileURL in urlsToDelete) {
            [_fileManager removeItemAtURL:fileURL error:nil];
        }

        // If our remaining disk cache exceeds a configured maximum size, perform a second
        // size-based cleanup pass.  We delete the oldest files first.
        // 計算出的緩存大小不滿足設置的CacheSize,作相應刪除處理
        if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) {
            // Target half of our maximum cache size for this cleanup pass.
            const NSUInteger desiredCacheSize = self.maxCacheSize / 2;

            // Sort the remaining cache files by their last modification time (oldest first).
            NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
                                                            usingComparator:^NSComparisonResult(id obj1, id obj2) {
                                                                return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
                                                            }];

            // Delete files until we fall below our desired cache size.
            for (NSURL *fileURL in sortedFiles) {
                if ([_fileManager removeItemAtURL:fileURL error:nil]) {
                    NSDictionary *resourceValues = cacheFiles[fileURL];
                    
                    //刪除文件時繼續(xù)計算當前緩存大小这难,直至滿足預定的大小才Break舟误。
                    NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
                    currentCacheSize -= [totalAllocatedSize unsignedIntegerValue];

                    if (currentCacheSize < desiredCacheSize) {
                        break;
                    }
                }
            }
        }
        if (completionBlock) {
            dispatch_async(dispatch_get_main_queue(), ^{
                completionBlock();
            });
        }
    });
}

這個刪除緩存圖片的邏輯處理得很巧妙,在實際工作中值得我們借鑒姻乓,
像我們現(xiàn)在的IM中刪除指定時間的聊天記錄等等嵌溢。

  1. 可以使用NSDirectoryEnumerator去檢索沙盒文件,處理計算文件大小蹋岩、文件數(shù)量等赖草。具體使用介紹看蘋果官方API文檔 DirectoryEnumerator
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市剪个,隨后出現(xiàn)的幾起案子秧骑,更是在濱河造成了極大的恐慌,老刑警劉巖扣囊,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乎折,死亡現(xiàn)場離奇詭異,居然都是意外死亡侵歇,警方通過查閱死者的電腦和手機骂澄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惕虑,“玉大人坟冲,你說我怎么就攤上這事〖纤欤” “怎么了樱衷?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長酒唉。 經(jīng)常有香客問我矩桂,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任侄榴,我火速辦了婚禮雹锣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘癞蚕。我一直安慰自己蕊爵,他們只是感情好,可當我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布桦山。 她就那樣靜靜地躺著攒射,像睡著了一般。 火紅的嫁衣襯著肌膚如雪恒水。 梳的紋絲不亂的頭發(fā)上会放,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天,我揣著相機與錄音钉凌,去河邊找鬼咧最。 笑死,一個胖子當著我的面吹牛御雕,可吹牛的內(nèi)容都是我干的矢沿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼酸纲,長吁一口氣:“原來是場噩夢啊……” “哼捣鲸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起福青,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤摄狱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后无午,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體媒役,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年宪迟,在試婚紗的時候發(fā)現(xiàn)自己被綠了酣衷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡次泽,死狀恐怖穿仪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情意荤,我是刑警寧澤啊片,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站玖像,受9級特大地震影響紫谷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一笤昨、第九天 我趴在偏房一處隱蔽的房頂上張望祖驱。 院中可真熱鬧,春花似錦瞒窒、人聲如沸捺僻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽匕坯。三九已至,卻和暖如春寇壳,著一層夾襖步出監(jiān)牢的瞬間醒颖,已是汗流浹背妻怎。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工壳炎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人逼侦。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓匿辩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親榛丢。 傳聞我的和親對象是個殘疾皇子铲球,可洞房花燭夜當晚...
    茶點故事閱讀 45,455評論 2 359

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