前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >iOS复习中有关SDWebImage可能知识点总结(2)

iOS复习中有关SDWebImage可能知识点总结(2)

作者头像
陈满iOS
发布于 2018-09-10 03:24:33
发布于 2018-09-10 03:24:33
87900
代码可运行
举报
文章被收录于专栏:陈满iOS陈满iOS
运行总次数:0
代码可运行
1. SDWebImage怎么实现缓存的?

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock {
  • 调用位置 -- SDWebImageManager.m
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                     options:(SDWebImageOptions)options
                                    progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                   completed:(nullable SDInternalCompletionBlock)completedBlock {

调用位置

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (void)storeImage:(nullable UIImage *)image
         imageData:(nullable NSData *)imageData
            forKey:(nullable NSString *)key
            toDisk:(BOOL)toDisk
        completion:(nullable SDWebImageNoParamsBlock)completionBlock {
  • 调用位置 -- SDWebImageManager.m
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                     options:(SDWebImageOptions)options
                                    progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                   completed:(nullable SDInternalCompletionBlock)completedBlock {

调用位置

  • 实现原理 --- SDImageCache.m

缓存数据

  • 其中,数据转换部分 原理为:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat {
    NSData *imageData = nil;
    if (self) {
#if SD_UIKIT || SD_WATCH
        int 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 channel
        if (imageFormat != SDImageFormatUndefined) {
            usePNG = (imageFormat == SDImageFormatPNG);
        }
        
        if (usePNG) {
            imageData = UIImagePNGRepresentation(self);
        } else {
            imageData = UIImageJPEGRepresentation(self, (CGFloat)1.0);
        }
#else
        NSBitmapImageFileType imageFileType = NSJPEGFileType;
        if (imageFormat == SDImageFormatGIF) {
            imageFileType = NSGIFFileType;
        } else if (imageFormat == SDImageFormatPNG) {
            imageFileType = NSPNGFileType;
        }
        
        imageData = [NSBitmapImageRep representationOfImageRepsInArray:self.representations
                                                             usingType:imageFileType
                                                            properties:@{}];
#endif
    }
    return imageData;
}

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (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 key
    NSString *cachePathForKey = [self defaultCachePathForKey:key];
    // transform to NSUrl
    NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
    
    [_fileManager createFileAtPath:cachePathForKey contents:imageData attributes:nil];
    
    // disable iCloud backup
    if (self.config.shouldDisableiCloud) {
        [fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
    }
}
2. SDWebImage下载后的图片在什么时候用到解码?

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

  • 内部API
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
+ (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image {
  • 调用位置1 -- SDWebImageDownloaderOperation.m代理1
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (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 GIFs
                if (!image.images) {
                    if (self.shouldDecompressImages) {
                        if (self.options & SDWebImageDownloaderScaleDownLargeImages) {
#if SD_UIKIT || SD_WATCH
                            image = [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
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
//省略...
  • 实现原理 -- SDWebImageDecoder.m
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
+ (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 alpha
        CGContextDrawImage(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这样封装了一个宏:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//主线程同步队列
#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);\
    }
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//用法
  dispatch_main_async_safe(^{
                    //需要执行的代码片段;
                });

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#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开头的,可见它仅仅管理下载的操作,没有缓存的管理功能。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.07.01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
入门微服务架构设计:由下单场景实现服务注册、发现以及调用
这里推荐一篇Java 异常的文章:Java程序员必备:深入剖析Java异常体系的核心架构 文章列总结Java异常的几种分类、举例使用方法以及常见应用场景,总结分析优缺点、最后设计异常工具类的最佳实践
菜菜的后端私房菜
2024/11/14
880
SpringCloud:Feign实现微服务之间相互请求
上篇文章说了通过RestTemplate实现微服务之间访问:https://blog.csdn.net/Ber_Bai/article/details/125460941,这篇文章将通过Feign实现微服务之间访问。
鳄鱼儿
2022/06/30
3870
SpringCloud集成Nacos实现服务发现和配置管理
本章节我通过在SpringCloud中写服务者(Provider,端口:9001)、消费者(Consumer,端口:9002),来演示服务发现。
麦洛
2021/03/23
2K0
SpringCloud集成Nacos实现服务发现和配置管理
Spring Cloud Gateway整合nacos实战(三)
Spring Cloud GateWay是Spring Cloud的⼀个全新项⽬,⽬标是取代Netflix Zuul,它基于Spring5.0+SpringBoot2.0+WebFlux(基于⾼性能的Reactor模式响应式通信框架Netty,异步⾮阻塞模型)等技术开发,性能⾼于Zuul,官⽅测试,GateWay是Zuul的1.6倍,旨在为微服务架构提供⼀种简单有效的统⼀的API路由管理⽅式。
Java技术债务
2022/08/09
2.6K0
Spring Cloud Gateway整合nacos实战(三)
SpringCloud 2020版本教程1:使用nacos作为注册中心和配置中心
本次教程旨在为读者提供2020版本的最佳实践方案,使用我认为最容易学习的组件,可能很多组件有很多替代方案,在这里不依依讲述。本次教程使用的组件如下:
方志朋
2022/01/06
8950
SpringCloud 2020版本教程1:使用nacos作为注册中心和配置中心
Spring Cloud Alibaba(三):工程搭建
学习Spring Cloud Alibaba,从工程搭建开始! Spring Boot <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>xxx</version> <relativePath/> </parent> 版本定义 <properties> <java.version>1.8</j
冯文议
2021/04/20
1K0
手把手教你搭建SpringCloudAlibaba微服务架构——集成Dubbo、Feign实现服务注册、远程服务调用(RPC)
Nacos肯定是要有的,上一篇已经介绍了Nacos的搭建以及配置中心的基本使用,而在这篇文章中Nacos扮演的是注册中心的角色,通过集成Dubbo与Feign完成RPC的调用,还没有搭建Nacos的小伙伴可以参考我的上一篇文章哦。
敲得码黛
2021/02/22
2.7K0
手把手教你搭建SpringCloudAlibaba微服务架构——集成Dubbo、Feign实现服务注册、远程服务调用(RPC)
客户端启动报错java.lang.IllegalArgumentException: no server available的解决方案 SpringCloud中 Nacos做注册中心
我的错误就在这里,百度了很久也修改了很多配置 才发现是自己写的有问题; 大家在配置nacos的配置中心的时候 注意写法(很重要)
默 语
2024/11/20
2400
客户端启动报错java.lang.IllegalArgumentException: no server available的解决方案 SpringCloud中 Nacos做注册中心
Spring Cloud 2020 版本最佳实践,你落伍了!
来源:ilovey.live/2021/09/ 26/springcloud2020/
芋道源码
2021/10/14
1.3K0
支持Nacos 2.1.0!这套Spring Cloud Gateway+Oauth2终极权限解决方案升级了!
在微服务系统中实现权限功能时,我们不应该把重复的权限校验功能集成到每个独立的API服务中去,而应该在网关做统一处理,然后通过认证中心去统一认证,这样才是优雅微服务权限解决方案!
macrozheng
2022/07/24
1.6K0
支持Nacos 2.1.0!这套Spring Cloud Gateway+Oauth2终极权限解决方案升级了!
SpringCloud 2.x学习笔记:18、使用Nacos作为服务注册发现组件(Greenwich版本)
https://github.com/alibaba/nacos/releases
程裕强
2019/07/01
5580
SpringCloud 2.x学习笔记:18、使用Nacos作为服务注册发现组件(Greenwich版本)
springcloud alibaba 集成 nacos注册中心配置使用
pom <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <shiro.version>1.7.1</shiro.version><!-- fhadmin.cn -->
FHAdmin
2022/03/04
3110
吐血推荐--SpringBoot与SpringCloud的版本对应说明,兼容说明
本文为joshua317原创文章,转载请注明:转载自joshua317博客 https://www.joshua317.com/article/139
joshua317
2021/09/26
9.1K0
吐血推荐--SpringBoot与SpringCloud的版本对应说明,兼容说明
手把手教你搭建SpringCloudAlibaba微服务架构——Nacos配置中心的搭建
我用的是Win10系统,为了演示方便,所以就直接把nacos搭在了本地,配置源暂时先用Nacos内嵌的,后面搭建nacos集群时会改用mysql来存储nacos配置信息。
敲得码黛
2021/02/22
2.3K0
手把手教你搭建SpringCloudAlibaba微服务架构——Nacos配置中心的搭建
Nacos 与项目整合
为什么要使用 bootstrap.yml 而不是 application.yml 呢?
啵啵肠
2023/11/20
2370
Spring Cloud Alibaba+Nacos 2.2.5.Release 的基本使用和采坑问
◆ 前言 Nacos 是构建以“服务”为中心的现代应用架构的服务基础设施, 支持几乎所有主流类型服务的发现、配置和管理,是目前微服务项目构建的主流服务注册组件。本 Chat 以构建商品中心项目为例
IT大咖说
2022/06/24
1.5K0
Spring Cloud Alibaba+Nacos 2.2.5.Release 的基本使用和采坑问
基于springcloud gateway + nacos实现灰度发布(reactive版)
灰度发布(又名金丝雀发布)是指在黑与白之间,能够平滑过渡的一种发布方式。在其上可以进行A/B testing,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。
lyb-geek
2020/04/26
3.1K0
微服务课程之SpringCloud 概述及微服务搭建
2,本章重点 微服务的概念,特点,优点,缺点 分布式框架(微服务)的演变过程 springcloud简介和特征 springcloud组件和体系架构 搭建父子项目,编写一个微服务 3,具体内容 3.1 微服务的概念 微服务(micro service),是一种架构风格,它将一个复杂的应用拆分成多个独立自治的服务,服务与服务间通过松耦合的形式交互。这些服务都可以单独的开发,测试,部署,运行;相互协作,更好完成原来大的业务系统的所有功能。 3.2 微服务的特点,优点和缺点 1)特点 解耦:服务做了拆分,相互影响会大大降低,符合软件设计的低耦合原则。 组件化:每个服务都相当于一个独立的组件,可以进行独立升级和扩展,可以被重复使用,节省人力成本。 业务能力强:职责明确,更专注于某一个业务 自治:一个微服务就是一个独立的实体,它可以独立部署、升级,服务与服务之间通过REST等形式的标准接口进行通信,并且一个微服务实例可以被替换成另一种实现,而对其它的微服务不产生影响。 敏捷性: 微服务可以敏捷开发(迭代)非常适合。 中国扶贫办->中国移动(华为,亚信)-> 精简版上线30天上线->每隔2-3周迭代一个新功能(修复旧版本的BUG),直到整个10个月全部完成 2)优点 易于开发和部署(单个项目) 启动快 局部更新,对整体影响很小 技术不受限制 按需求伸缩 devops(develop开发 operations运维) 促进开发,运维和测试(质检QA)的协作 DevOps(Development和Operations的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。 它是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。透过自动化“软件交付”和“架构变更”的流程,来使得构建、测试、发布软件能够更加地快捷、频繁和可靠。
张哥编程
2024/12/13
2390
微服务课程之SpringCloud 概述及微服务搭建
手把手教你将Eureka升级Nacos注册中心
由于原有SpringCloud体系版本比较老,最初的注册中心使用的Eureka后期官方无升级方案,配置中心无法在线管理配置,还有实时上下线的问题,因此需要将原有系统的Eureka服务升级Nacos注册心服务。
欢醉
2023/02/09
1.1K0
Spring Cloud Alibaba 系列之 Seata 分布式事务 demo
  在上一篇博客 《Spring Cloud Alibaba 系列之 Seata 介绍》 介绍了 Seata,在这一篇中我们来看看怎么使用。以一个用户购买商品的微服务示例开始,整个业务逻辑由3个微服务提供支持:仓储服务:对给定的商品扣除仓储数量。订单服务:根据采购需求创建订单。帐户服务:从用户帐户中扣除余额。
Demo_Null
2020/12/07
1.3K0
Spring Cloud Alibaba 系列之 Seata 分布式事务 demo
推荐阅读
相关推荐
入门微服务架构设计:由下单场景实现服务注册、发现以及调用
更多 >
LV.1
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档