iOS复习和面试有关SDWebImage可能知识点2
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
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13