iOS圖片加載框架的基本思路

看完這篇文章從零開(kāi)始打造一個(gè)iOS圖片加載框架(一),詳細(xì)的介紹了搭建一個(gè)iOS圖片加載框架基本思路,再回看YYWebImage,SDWebImage和PINRemoteImage
框架的底層,發(fā)現(xiàn)實(shí)現(xiàn)的圖片加載的思路基本相同拐辽,只是細(xì)節(jié)上不同而已。
推薦文章:
從零開(kāi)始打造一個(gè)iOS圖片加載框架(一)
從零開(kāi)始打造一個(gè)iOS圖片加載框架(二)
從零開(kāi)始打造一個(gè)iOS圖片加載框架(三)

*SDWebImage底層實(shí)現(xiàn)簡(jiǎn)析
  • 關(guān)鍵代碼分析
- (void)callCacheProcessForOperation:(nonnull SDWebImageCombinedOperation *)operation
                                 url:(nonnull NSURL *)url
                             options:(SDWebImageOptions)options
                             context:(nullable SDWebImageContext *)context
                            progress:(nullable SDImageLoaderProgressBlock)progressBlock
                           completed:(nullable SDInternalCompletionBlock)completedBlock {
    // Check whether we should query cache
    BOOL shouldQueryCache = (options & SDWebImageFromLoaderOnly) == 0;
    if (shouldQueryCache) {
        id<SDWebImageCacheKeyFilter> cacheKeyFilter = context[SDWebImageContextCacheKeyFilter];
        NSString *key = [self cacheKeyForURL:url cacheKeyFilter:cacheKeyFilter];
        @weakify(operation);
       //開(kāi)啟一個(gè)線程擦酌,執(zhí)行通過(guò)key從緩存和磁盤(pán)中獲取圖片的任務(wù)
        operation.cacheOperation = [self.imageCache queryImageForKey:key options:options context:context completion:^(UIImage * _Nullable cachedImage, NSData * _Nullable cachedData, SDImageCacheType cacheType) {
            @strongify(operation);
            //如果線程已經(jīng)取消或operation==nil俱诸,說(shuō)明已經(jīng)從緩存和磁盤(pán)中獲取到圖片,return
            if (!operation || operation.isCancelled) {
                [self safelyRemoveOperationFromRunning:operation];
                return;
            }
            // Continue download process
            //如果沒(méi)有從緩存和磁盤(pán)中獲取到圖片赊舶,則直接網(wǎng)絡(luò)中下載
            [self callDownloadProcessForOperation:operation url:url options:options context:context cachedImage:cachedImage cachedData:cachedData cacheType:cacheType progress:progressBlock completed:completedBlock];
        }];
    } else {
        // Continue download process
        [self callDownloadProcessForOperation:operation url:url options:options context:context cachedImage:nil cachedData:nil cacheType:SDImageCacheTypeNone progress:progressBlock completed:completedBlock];
    }
}

- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context done:(nullable SDImageCacheQueryCompletionBlock)doneBlock {
    if (!key) {
        if (doneBlock) {
            doneBlock(nil, nil, SDImageCacheTypeNone);
        }
        return nil;
    }
    
    id<SDImageTransformer> transformer = context[SDWebImageContextImageTransformer];
    if (transformer) {
        // grab the transformed disk image if transformer provided
        NSString *transformerKey = [transformer transformerKey];
        key = SDTransformedKeyForKey(key, transformerKey);
    }
    
    // First check the in-memory cache...  //從內(nèi)存中獲取緩存
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    BOOL shouldQueryMemoryOnly = (image && !(options & SDImageCacheQueryMemoryData));
    if (shouldQueryMemoryOnly) {
        if (doneBlock) {
            doneBlock(image, nil, SDImageCacheTypeMemory);
        }
        return nil;
    }
    
    // Second check the disk cache...
    NSOperation *operation = [NSOperation new];
    // Check whether we need to synchronously query disk
    // 1. in-memory cache hit & memoryDataSync
    // 2. in-memory cache miss & diskDataSync
    BOOL shouldQueryDiskSync = ((image && options & SDImageCacheQueryMemoryDataSync) ||
                                (!image && options & SDImageCacheQueryDiskDataSync));
    void(^queryDiskBlock)(void) =  ^{
        if (operation.isCancelled) {
            // do not call the completion if cancelled
            return;
        }
        //線程池
        @autoreleasepool {
            // 從磁盤(pán)中獲取緩存
            NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
            UIImage *diskImage;
            SDImageCacheType cacheType = SDImageCacheTypeNone;
            NSLog(@"當(dāng)前的Key:%@",key);
            if (image) {
                // the image is from in-memory cache, but need image data
                diskImage = image;
                cacheType = SDImageCacheTypeMemory;
                NSLog(@"從內(nèi)存中獲取緩存:%@",image.description);
            } else if (diskData) {
                cacheType = SDImageCacheTypeDisk;
                // decode image data only if in-memory cache missed
                NSLog(@"從磁盤(pán)中獲取緩存:%@",diskData.description);
                diskImage = [self diskImageForKey:key data:diskData options:options context:context];
                if (diskImage && self.config.shouldCacheImagesInMemory) {
                    NSUInteger cost = diskImage.sd_memoryCost;
                    [self.memCache setObject:diskImage forKey:key cost:cost];
                }
            }
            
            if (doneBlock) {
                if (shouldQueryDiskSync) {
                    doneBlock(diskImage, diskData, cacheType);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        doneBlock(diskImage, diskData, cacheType);
                    });
                }
            }
        }
    };
    
    // Query in ioQueue to keep IO-safe
    if (shouldQueryDiskSync) {
        dispatch_sync(self.ioQueue, queryDiskBlock);
    } else {
        dispatch_async(self.ioQueue, queryDiskBlock);
    }
    
    return operation;
}
// Download process
- (void)callDownloadProcessForOperation:(nonnull SDWebImageCombinedOperation *)operation
                                    url:(nonnull NSURL *)url
                                options:(SDWebImageOptions)options
                                context:(SDWebImageContext *)context
                            cachedImage:(nullable UIImage *)cachedImage
                             cachedData:(nullable NSData *)cachedData
                              cacheType:(SDImageCacheType)cacheType
                               progress:(nullable SDImageLoaderProgressBlock)progressBlock
                              completed:(nullable SDInternalCompletionBlock)completedBlock {
    // Check whether we should download image from network
    BOOL shouldDownload = (options & SDWebImageFromCacheOnly) == 0;
    shouldDownload &= (!cachedImage || options & SDWebImageRefreshCached);
    shouldDownload &= (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]);
    shouldDownload &= [self.imageLoader canRequestImageForURL:url];
    if (shouldDownload) {
        if (cachedImage && options & SDWebImageRefreshCached) {
            // If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
            // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
            [self callCompletionBlockForOperation:operation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
            // Pass the cached image to the image loader. The image loader should check whether the remote image is equal to the cached image.
            SDWebImageMutableContext *mutableContext;
            if (context) {
                mutableContext = [context mutableCopy];
            } else {
                mutableContext = [NSMutableDictionary dictionary];
            }
            mutableContext[SDWebImageContextLoaderCachedImage] = cachedImage;
            context = [mutableContext copy];
        }
        
        // `SDWebImageCombinedOperation` -> `SDWebImageDownloadToken` -> `downloadOperationCancelToken`, which is a `SDCallbacksDictionary` and retain the completed block below, so we need weak-strong again to avoid retain cycle
        @weakify(operation);
        operation.loaderOperation = [self.imageLoader requestImageWithURL:url options:options context:context progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
            @strongify(operation);
            if (!operation || operation.isCancelled) {
                // Do nothing if the operation was cancelled
                // See #699 for more details
                // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data
            } else if (cachedImage && options & SDWebImageRefreshCached && [error.domain isEqualToString:SDWebImageErrorDomain] && error.code == SDWebImageErrorCacheNotModified) {
                // Image refresh hit the NSURLCache cache, do not call the completion block
            } else if (error) {
                [self callCompletionBlockForOperation:operation completion:completedBlock error:error url:url];
                BOOL shouldBlockFailedURL = [self shouldBlockFailedURLWithURL:url error:error];
                
                if (shouldBlockFailedURL) {
                    SD_LOCK(self.failedURLsLock);
                    [self.failedURLs addObject:url];
                    SD_UNLOCK(self.failedURLsLock);
                }
            } else {
                if ((options & SDWebImageRetryFailed)) {
                    SD_LOCK(self.failedURLsLock);
                    [self.failedURLs removeObject:url];
                    SD_UNLOCK(self.failedURLsLock);
                }
                
                [self callStoreCacheProcessForOperation:operation url:url options:options context:context downloadedImage:downloadedImage downloadedData:downloadedData finished:finished progress:progressBlock completed:completedBlock];
            }
            
            if (finished) {
                [self safelyRemoveOperationFromRunning:operation];
            }
        }];
    } else if (cachedImage) {
        [self callCompletionBlockForOperation:operation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
        [self safelyRemoveOperationFromRunning:operation];
    } else {
        // Image not in cache and download disallowed by delegate
        [self callCompletionBlockForOperation:operation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
        [self safelyRemoveOperationFromRunning:operation];
    }
}
  • SDWebImage 流程


    SDWebImage 流程.jpg
面試題

1.使用SDWebImage加載圖片睁搭,有一個(gè)圖片的url鏈接第一次加載完了后,后臺(tái)將url鏈接對(duì)應(yīng)的圖片內(nèi)容更改(即保持url不變)笼平,使用SDWebImage去再次顯示時(shí)园骆,圖片會(huì)變嗎?如果會(huì),請(qǐng)說(shuō)明理由寓调,如果不會(huì)锌唾,如何才能顯示服務(wù)器里面真正存放的圖片?

1.該題考察的是對(duì)SDWebImage的底層實(shí)現(xiàn)夺英,首先SD會(huì)將這個(gè)url作為Key晌涕,從內(nèi)存緩存以及磁盤(pán)緩存中去獲取該圖片,如果內(nèi)存緩存以及磁盤(pán)緩存都沒(méi)有找到該Key對(duì)應(yīng)圖片痛悯,則才會(huì)執(zhí)行網(wǎng)絡(luò)下載的步驟余黎。
2.因?yàn)閡rl不變,所以key不變,所以第二次加載會(huì)從磁盤(pán)中得到該圖片直接顯示载萌,圖片不會(huì)發(fā)生變化惧财。
3.如果確認(rèn)url內(nèi)容發(fā)生改變,[[SDImageCache sharedImageCache]removeImageForKey:url];可先清除該url上的緩存巡扇。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市垮衷,隨后出現(xiàn)的幾起案子厅翔,更是在濱河造成了極大的恐慌,老刑警劉巖帘靡,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件知给,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡描姚,警方通過(guò)查閱死者的電腦和手機(jī)涩赢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)轩勘,“玉大人筒扒,你說(shuō)我怎么就攤上這事“硌埃” “怎么了花墩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)澄步。 經(jīng)常有香客問(wèn)我冰蘑,道長(zhǎng),這世上最難降的妖魔是什么村缸? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任祠肥,我火速辦了婚禮,結(jié)果婚禮上梯皿,老公的妹妹穿的比我還像新娘仇箱。我一直安慰自己,他們只是感情好东羹,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布剂桥。 她就那樣靜靜地躺著,像睡著了一般属提。 火紅的嫁衣襯著肌膚如雪权逗。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天冤议,我揣著相機(jī)與錄音旬迹,去河邊找鬼。 笑死求类,一個(gè)胖子當(dāng)著我的面吹牛奔垦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尸疆,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼椿猎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼惶岭!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起犯眠,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤按灶,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后筐咧,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體鸯旁,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年量蕊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了铺罢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡残炮,死狀恐怖韭赘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情势就,我是刑警寧澤泉瞻,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站苞冯,受9級(jí)特大地震影響袖牙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜舅锄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一鞭达、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧巧娱,春花似錦、人聲如沸烘贴。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)桨踪。三九已至老翘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锻离,已是汗流浹背铺峭。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汽纠,地道東北人卫键。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像虱朵,于是被迫代替她去往敵國(guó)和親莉炉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钓账,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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

  • 在那時(shí)候只有開(kāi)放式空中纜車(chē),準(zhǔn)備坐纜車(chē)到云南民族村去絮宁,為了搶拍沿途在滇池上空美麗的風(fēng)光梆暮,把已拍攝好的相片,...
    余夫55閱讀 969評(píng)論 3 11
  • 嗨,Miss張: 好久不見(jiàn)窘游,上次給你寫(xiě)信是啥時(shí)候嘞唠椭,你還記得不?沒(méi)記錯(cuò)得話應(yīng)該是13年來(lái)著吧张峰,時(shí)間可真是快呀泪蔫,沒(méi)用...
    道格次狼閱讀 1,312評(píng)論 0 0