• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

iOS复习和面试有关SDWebImage可能知识点2

武飞扬头像
网络安全敢敢
帮助1

1. SDWebImage怎么实现缓存的?


分为内存缓存(利用SDImageCache类的NSCache属性),磁盘缓存(利用NSFileManager),和操作缓存(利用runtime关联的字典属性)。下载之前先查询缓存,没有就下载并在下载后保存图片到缓存。

(1). 查询图片缓存 的内部API

- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock { 
  • 调用位置 – SDWebImageManager.m
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)optionsprogress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDInternalCompletionBlock)completedBlock { 

学新通

(2). 保存图片到缓存 的内部API

- (void)storeImage:(nullable UIImage *)image imageData:(nullable NSData *)imageDataforKey:(nullable NSString *)keytoDisk:(BOOL)toDiskcompletion:(nullable SDWebImageNoParamsBlock)completionBlock { 
  • 调用位置 – SDWebImageManager.m
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)optionsprogress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDInternalCompletionBlock)completedBlock { 

学新通

  • 实现原理 — SDImageCache.m

学新通

  • 其中,数据转换部分 原理为:
- (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat {NSData *imageData = nil;if (self) {
#if SD_UIKIT || SD_WATCHint alphaInfo = CGImageGetAlphaInfo(self.CGImage);BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||alphaInfo == kCGImageAlphaNoneSkipFirst ||alphaInfo == kCGImageAlphaNoneSkipLast);BOOL usePNG = hasAlpha;// the imageFormat param has priority here. But if the format is undefined, we relly on the alpha channelif (imageFormat != SDImageFormatUndefined) {usePNG = (imageFormat == SDImageFormatPNG);}if (usePNG) {imageData = UIImagePNGRepresentation(self);} else {imageData = UIImageJPEGRepresentation(self, (CGFloat)1.0);}
#elseNSBitmapImageFileType imageFileType = NSJPEGFileType;if (imageFormat == SDImageFormatGIF) {imageFileType = NSGIFFileType;} else if (imageFormat == SDImageFormatPNG) {imageFileType = NSPNGFileType;}imageData = [NSBitmapImageRep representationOfImageRepsInArray:self.representations usingType:imageFileTypeproperties:@{}];
#endif}return imageData;
} 

其中,保存到沙盒部分 原理为:

- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key {if (!imageData || !key) {return;}[self checkIfQueueIsIOQueue];if (![_fileManager fileExistsAtPath:_diskCachePath]) {[_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];}// get cache Path for image keyNSString *cachePathForKey = [self defaultCachePathForKey:key];// transform to NSUrlNSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];[_fileManager createFileAtPath:cachePathForKey contents:imageData attributes:nil];// disable iCloud backupif (self.config.shouldDisableiCloud) {[fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];}
} 

2. SDWebImage下载后的图片在什么时候用到解码?


在NSURLSession下载完成后的代理方法中,具体文件是SDWebImageDownloaderOperation.m。

  • 内部API
  (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image { 
  • 调用位置1 – SDWebImageDownloaderOperation.m代理1
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {@synchronized(self) {self.dataTask = nil;dispatch_async(dispatch_get_main_queue(), ^{[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];if (!error) {[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadFinishNotification object:self];}});}if (error) {[self callCompletionBlocksWithError:error];} else {if ([self callbacksForKey:kCompletedCallbackKey].count > 0) {/** *See #1608 and #1623 - apparently, there is a race condition on `NSURLCache` that causes a crash *Limited the calls to `cachedResponseForRequest:` only for cases where we should ignore the cached response *and images for which responseFromCached is YES (only the ones that cannot be cached). *Note: responseFromCached is set to NO inside `willCacheResponse:`. This method doesn't get called for large images or images behind authentication */if (self.options & SDWebImageDownloaderIgnoreCachedResponse && responseFromCached && [[NSURLCache sharedURLCache] cachedResponseForRequest:self.request]) {// hack[self callCompletionBlocksWithImage:nil imageData:nil error:nil finished:YES];} else if (self.imageData) {UIImage *image = [UIImage sd_imageWithData:self.imageData];NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];image = [self scaledImageForKey:key image:image];// Do not force decoding animated GIFsif (!image.images) {if (self.shouldDecompressImages) {if (self.options & SDWebImageDownloaderScaleDownLargeImages) {
#if SD_UIKIT || SD_WATCHimage = [UIImage decodedAndScaledDownImageWithImage:image];[self.imageData setData:UIImagePNGRepresentation(image)];
#endif} else {image = [UIImage decodedImageWithImage:image];}}}if (CGSizeEqualToSize(image.size, CGSizeZero)) {[self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}]];} else {[self callCompletionBlocksWithImage:image imageData:self.imageData error:nil finished:YES];}} else {[self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}]];}}}[self done];
} 
  • 调用位置2 – SDWebImageDownloaderOperation.m代理1
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
//省略... 
  • 实现原理 – SDWebImageDecoder.m
  (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image {if (![UIImage shouldDecodeImage:image]) {return image;}// autorelease the bitmap context and all vars to help system to free memory when there are memory warning.// on iOS7, do not forget to call [[SDImageCache sharedImageCache] clearMemory];@autoreleasepool{CGImageRef imageRef = image.CGImage;CGColorSpaceRef colorspaceRef = [UIImage colorSpaceForImageRef:imageRef];size_t width = CGImageGetWidth(imageRef);size_t height = CGImageGetHeight(imageRef);size_t bytesPerRow = kBytesPerPixel * width;// kCGImageAlphaNone is not supported in CGBitmapContextCreate.// Since the original image here has no alpha info, use kCGImageAlphaNoneSkipLast// to create bitmap graphics contexts without alpha info.CGContextRef context = CGBitmapContextCreate(NULL, width, height, kBitsPerComponent, bytesPerRow, colorspaceRef, kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);if (context == NULL) {return image;}// Draw the image into the context and retrieve the new bitmap image without alphaCGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context);UIImage *imageWithoutAlpha = [UIImage imageWithCGImage:imageRefWithoutAlpha scale:image.scale orientation:image.imageOrientation];CGContextRelease(context);CGImageRelease(imageRefWithoutAlpha);return imageWithoutAlpha;}
} 

3. 怎样安全地在主线程执行一个Block?

有时候会把将要执行的内容放到主线程里面执行,但如果已经是主线程里面的代码调用dispatch_async的时候偶尔会出现crash,所以就需要判断是否已经在主线程里面了。

老版本的SDWebImage这样封装了一个宏:

//主线程同步队列
#define dispatch_main_sync_safe(block)\if ([NSThread isMainThread]) {\block();\} else {\dispatch_sync(dispatch_get_main_queue(), block);\}
//主线程异步队列
#define dispatch_main_async_safe(block)\if ([NSThread isMainThread]) {\block();\} else {\dispatch_async(dispatch_get_main_queue(), block);\} 
//用法dispatch_main_async_safe(^{//需要执行的代码片段;}); 

新版本的SDWebImage是这样封装的宏:

#ifndef dispatch_main_async_safe
#define dispatch_main_async_safe(block)\if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\block();\} else {\dispatch_async(dispatch_get_main_queue(), block);\}
#endif 

其中的strcmp是字符串比较函数:

int strcmp(char *str1, char *str2) 当str1>str2时,返回一个正数; 当str1<str2时,返回一个负数; 当str1=str2时,返回0。 最后应该注意的是:两个字符串比较时,是按asiic码大小逐个比较的,当发现某一个大或者小时,就停止比较、返回一个值。否则比较到最后一个字母。

注意的问题是,宏里面的block是无法打断点调试的。你如果步进查看,可以发现会跳到汇编的文件里面步进。

4. 怎样区分SDWebImageDownloader和SDWebImageManager的工作?

  • SDWebImageManager提供的关键API是loadImageWithURL开头的,负责加载的,加载load这个词跟下载download不同,比它更广,加载负责管理下载之前的操作:* 管理下载操作的开始和取消* 下载之前查询图片的内存缓存和磁盘缓存* 下载之后保存图片到内存缓存和磁盘缓存* 返回一个操作对象给上级对象UIImageView WebCache作为操作缓存数组属性中去
  • SDWebImageDownloader提供的关键API是downloadImageWithURL开头的,可见它仅仅管理下载的操作,没有缓存的管理功能。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgcackf
系列文章
更多 icon
同类精品
更多 icon
继续加载