探究react-native 源碼的圖片緩存

本文為xcode模擬器測(cè)試,rn版本0.44.3
突然想學(xué)習(xí)下RN是如何封裝ios中的UIImage的,看著看著發(fā)現(xiàn)圖片的緩存問(wèn)題是個(gè)坑绢片。。岛琼。
先看js端圖片使用的三種方式底循,依次排序1、2槐瑞、3

 <Image source={{uri:url}} style={{width:200,height:200}}/>    1熙涤、 加載遠(yuǎn)程圖片
 <Image source={{uri:'1.png'}} style={{width:50,height:50}}/>  2、加載xcode中圖片
 <Image source={require('../../../Resources/Images/Contact/conact_searchIcon@3x.png')}/>  3困檩、加載js中圖片

1祠挫、2必須設(shè)置圖片寬高,3不需設(shè)置悼沿。

對(duì)應(yīng)的ios原生端文件是RCTImageViewManager等舔,暴露的屬性

    RCT_REMAP_VIEW_PROPERTY(source, imageSources, NSArray<RCTImageSource *>);

就是js中Image組件的屬性source,在js中設(shè)置source會(huì)觸發(fā)該屬性的setter方法糟趾。進(jìn)入RCTImageView的

- (void)setImageSources:(NSArray<RCTImageSource *> *)imageSources
 {
         if (![imageSources isEqual:_imageSources]) {
               _imageSources = [imageSources copy];
               [self reloadImage];
         }
 }

通過(guò)此方法中斷點(diǎn)打印imageSources慌植,依次得到下面結(jié)果:

可見(jiàn),Image組件加載圖片都是采用URL的形式义郑,將圖片當(dāng)作網(wǎng)絡(luò)資源蝶柿。不同的是URL的類(lèi)型:

   加載網(wǎng)絡(luò)上圖片         :  http://
   加載xcode資源         :  file://
   加載js中圖片           :   http://localhost:8081

追蹤setter方法,到RCTImageLoader.m中的如下方法

- (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest
                                                      size:(CGSize)size
                                                     scale:(CGFloat)scale
                                                   clipped:(BOOL)clipped
                                                resizeMode:(RCTResizeMode)resizeMode
                                             progressBlock:(RCTImageLoaderProgressBlock)progressBlock
                                          partialLoadBlock:(RCTImageLoaderPartialLoadBlock)partialLoadBlock
                                           completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
{
    __block volatile uint32_t cancelled = 0;
  __block dispatch_block_t cancelLoad = nil;
  dispatch_block_t cancellationBlock = ^{
    dispatch_block_t cancelLoadLocal = cancelLoad;
    if (cancelLoadLocal && !cancelled) {
      cancelLoadLocal();
    }
    OSAtomicOr32Barrier(1, &cancelled);
  };
  // 下載圖片完成后回調(diào)
  __weak RCTImageLoader *weakSelf = self;
  void (^completionHandler)(NSError *, id, BOOL, NSString *) = ^(NSError *error, id imageOrData, BOOL cacheResult, NSString *fetchDate) {
    __typeof(self) strongSelf = weakSelf;
    if (cancelled || !strongSelf) {
      return;
    }
     //  如果imageOrData是圖片類(lèi)型非驮,則直接回調(diào)
     //  此處交汤,如果是第二種情況,則會(huì)滿(mǎn)足院尔,其他情況繼續(xù)走下面方法
    if (!imageOrData || [imageOrData isKindOfClass:[UIImage class]]) {
      cancelLoad = nil;
      completionBlock(error, imageOrData);
      return;
    }
  
    // 在內(nèi)存中查看是否存在該url對(duì)應(yīng)的字節(jié)碼圖片
    if (cacheResult) {
      UIImage *image = [[strongSelf imageCache] imageForUrl:imageURLRequest.URL.absoluteString
                                                       size:size
                                                      scale:scale
                                                 resizeMode:resizeMode
                                               responseDate:fetchDate];
      if (image) {
        cancelLoad = nil;
        completionBlock(nil, image);
        return;
      }
    }

      // 若沒(méi)有緩存蜻展,則將圖片解壓喉誊,再將解壓后圖片緩存block
    RCTImageLoaderCompletionBlock decodeCompletionHandler = ^(NSError *error_, UIImage *image) {
      if (cacheResult && image) {
        // Store decoded image in cache
        [[strongSelf imageCache] addImageToCache:image
                                             URL:imageURLRequest.URL.absoluteString
                                            size:size
                                           scale:scale
                                      resizeMode:resizeMode
                                    responseDate:fetchDate];
      }

      cancelLoad = nil;
      completionBlock(error_, image);
    };
     // 具體的解壓過(guò)程
    cancelLoad = [strongSelf decodeImageData:imageOrData
                                      size:size
                                     scale:scale
                                   clipped:clipped
                                resizeMode:resizeMode
                           completionBlock:decodeCompletionHandler];
  };
   //  走具體的方法加載圖片,1纵顾、3種情況用網(wǎng)絡(luò)請(qǐng)求下載伍茄,2情況加載本地文件
  cancelLoad = [self _loadImageOrDataWithURLRequest:imageURLRequest
                                               size:size
                                              scale:scale
                                         resizeMode:resizeMode
                                      progressBlock:progressBlock
                                   partialLoadBlock:partialLoadBlock
                                    completionBlock:completionHandler];
  return cancellationBlock;
}

具體的緩存類(lèi)是RCTImageCache,采用NSCache緩存施逾,方法

- (void)addImageToCache:(UIImage *)image
                 forKey:(NSString *)cacheKey
{
  if (!image) {
    return;
  }
  CGFloat bytes = image.size.width * image.size.height * image.scale * image.scale * 4;
  if (bytes <= RCTMaxCachableDecodedImageSizeInBytes) {
    [self->_decodedImageCache setObject:image
                                 forKey:cacheKey
                                   cost:bytes];
  }
}

RCTMaxCachableDecodedImageSizeInBytes是個(gè)常量敷矫,為1048576,也就是只緩存小于1MB的圖片汉额。
問(wèn)題出在cacheKey曹仗,查看緩存key的方法

static NSString *RCTCacheKeyForImage(NSString *imageTag, CGSize size, CGFloat scale,
                                     RCTResizeMode resizeMode, NSString *responseDate)
{
    return [NSString stringWithFormat:@"%@|%g|%g|%g|%zd|%@",
            imageTag, size.width, size.height, scale, resizeMode, responseDate];
}

緩存key的生成方法中包含了responseDate,responseDate是網(wǎng)絡(luò)請(qǐng)求時(shí)返回來(lái)的

responseDate = ((NSHTTPURLResponse *)response).allHeaderFields[@"Date"];

1蠕搜、3方式每次加載都是一個(gè)網(wǎng)絡(luò)請(qǐng)求怎茫,那么網(wǎng)絡(luò)請(qǐng)求的時(shí)間總是變化的,于是responseDate是變化的妓灌,cacheKey不唯一轨蛤,所以雖然系統(tǒng)做了圖片的緩存,但是每次取出的都為nil虫埂,緩存無(wú)效祥山。

2方式加載具體方法在RCTLocalAssetImageLoader.m中,其調(diào)用的是RCTUtils的RCTImageFromLocalAssetURL方法

UIImage *__nullable RCTImageFromLocalAssetURL(NSURL *imageURL)
{
// .....省略各種處理
  UIImage *image = nil;
  if (bundle) {
    image = [UIImage imageNamed:imageName inBundle:bundle compatibleWithTraitCollection:nil];
  } else {
    image = [UIImage imageNamed:imageName];
  }
// .....省略各種處理
  return image;
}

可見(jiàn)是采用[UIImage imageNamed:imageName]的方式加載xcode自帶的圖片掉伏,這個(gè)是有內(nèi)存緩存的缝呕。

綜上,對(duì)react-native圖片加載
1斧散、3情況供常,沒(méi)有內(nèi)存緩存
2情況有系統(tǒng)默認(rèn)的內(nèi)存緩存
所有情況都沒(méi)有磁盤(pán)緩存

想讓內(nèi)存緩存生效,只需要改變cacheKey的生成規(guī)則即可颅湘。

補(bǔ)充:沙盒下面的Library/Caches/項(xiàng)目bunderId號(hào)/fsCachedData文件夾里面會(huì)磁盤(pán)緩存大于一定值(測(cè)試約為5kb)的圖片和文件话侧,這個(gè)是NSURLSession網(wǎng)絡(luò)請(qǐng)求系統(tǒng)默認(rèn)的緩存類(lèi)NSURLCache自動(dòng)生成的,非圖片的磁盤(pán)緩存闯参。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瞻鹏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鹿寨,更是在濱河造成了極大的恐慌新博,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脚草,死亡現(xiàn)場(chǎng)離奇詭異赫悄,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)埂淮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)姑隅,“玉大人,你說(shuō)我怎么就攤上這事倔撞〗惭觯” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵痪蝇,是天一觀的道長(zhǎng)鄙陡。 經(jīng)常有香客問(wèn)我,道長(zhǎng)躏啰,這世上最難降的妖魔是什么趁矾? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮给僵,結(jié)果婚禮上毫捣,老公的妹妹穿的比我還像新娘。我一直安慰自己想际,他們只是感情好培漏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著胡本,像睡著了一般。 火紅的嫁衣襯著肌膚如雪畸悬。 梳的紋絲不亂的頭發(fā)上侧甫,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天,我揣著相機(jī)與錄音蹋宦,去河邊找鬼披粟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛冷冗,可吹牛的內(nèi)容都是我干的守屉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蒿辙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拇泛!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起思灌,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤俺叭,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后泰偿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體熄守,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了裕照。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片攒发。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖晋南,靈堂內(nèi)的尸體忽然破棺而出惠猿,到底是詐尸還是另有隱情,我是刑警寧澤搬俊,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布紊扬,位于F島的核電站,受9級(jí)特大地震影響唉擂,放射性物質(zhì)發(fā)生泄漏餐屎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一玩祟、第九天 我趴在偏房一處隱蔽的房頂上張望腹缩。 院中可真熱鬧,春花似錦空扎、人聲如沸藏鹊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)盘寡。三九已至,卻和暖如春撮慨,著一層夾襖步出監(jiān)牢的瞬間竿痰,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工砌溺, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留影涉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓规伐,卻偏偏與公主長(zhǎng)得像蟹倾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子猖闪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

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