小編給大家分享一下react-native源碼圖片緩存問(wèn)題的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
創(chuàng)新互聯(lián)專注于南湖企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城網(wǎng)站制作。南湖網(wǎng)站建設(shè)公司,為南湖等地區(qū)提供建站服務(wù)。全流程按需求定制開(kāi)發(fā),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
為xcode模擬器測(cè)試,rn版本0.44.3
突然想學(xué)習(xí)下RN是如何封裝ios中的UIImage的,看著看著發(fā)現(xiàn)圖片的緩存問(wèn)題是個(gè)坑。。。
先看js端圖片使用的三種方式,依次排序1、2、3
// 1、 加載遠(yuǎn)程圖片 //2、加載xcode中圖片 //3、加載js中圖片
1、2必須設(shè)置圖片寬高,3不需設(shè)置。
對(duì)應(yīng)的ios原生端文件是RCTImageViewManager,暴露的屬性
RCT_REMAP_VIEW_PROPERTY(source, imageSources, NSArray);
就是js中Image組件的屬性source,在js中設(shè)置source會(huì)觸發(fā)該屬性的setter方法。進(jìn)入RCTImageView的
- (void)setImageSources:(NSArray*)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的類型:
加載網(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是圖片類型,則直接回調(diào) // 此處,如果是第二種情況,則會(huì)滿足,其他情況繼續(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; }
具體的緩存類是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)的
復(fù)制代碼 代碼如下:
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)有磁盤緩存
想讓內(nèi)存緩存生效,只需要改變cacheKey的生成規(guī)則即可。
補(bǔ)充:沙盒下面的Library/Caches/項(xiàng)目bunderId號(hào)/fsCachedData文件夾里面會(huì)磁盤緩存大于一定值(測(cè)試約為5kb)的圖片和文件,這個(gè)是NSURLSession網(wǎng)絡(luò)請(qǐng)求系統(tǒng)默認(rèn)的緩存類NSURLCache自動(dòng)生成的,非圖片的磁盤緩存。
以上是“react-native源碼圖片緩存問(wèn)題的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!