ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

JASONETTE-iOS 集成指南:SDWebImage 3.8 远程图片异步加载与缓存实战

JASONETTE-iOS 集成指南:SDWebImage 3.8 远程图片异步加载与缓存实战
  • 移动开发
  • 前端

【免费下载链接】JASONETTE-iOS

📡 Native App over HTTP, on iOS

项目地址:https://gitcode.com/gh_mirrors/ja/JASONETTE-iOS
点击查看免费下载

导读

本文以 JASONETTE-iOS 仓库中随包引入的 SDWebImage(版本 3.8.1)官方文档为骨架,结合仓库源码中的真实调用点,系统讲解这套 iOS 图片加载框架的核心能力与集成方式:从UIImageView+WebCache的一行式异步加载,到SDWebImageManager的下载/缓存调度,再到SDImageCache的内存与磁盘两级缓存、SDWebImageDownloader的独立下载器,以及 CocoaPods/Carthage/手动三种安装路径。读完本文,你将掌握在 iOS 应用中安全、高效地加载并缓存网络图片的完整实战方案,并能看懂 JASONETTE-iOS 中所有图片相关组件(导航图标、背景图、列表缩略图、音频封面等)的底层实现。

SDWebImage 是什么:为 UIImageView 而生的远程图片加载库

SDWebImage 是一个 Objective-C 库,核心是给UIImageView提供一个 category,使其直接支持加载来自网络的远程图片,同时提供一系列配套能力。官方文档(app/Pods/SDWebImage/README.md)明确列出的功能包括:

  • 为 Cocoa Touch 框架增加UIImageView分类,集成 Web 图片加载与缓存管理;
  • 异步图片下载器;
  • 异步的内存 + 磁盘两级缓存,并带自动缓存过期处理;
  • 动画 GIF 支持;
  • WebP 格式支持;
  • 后台图片解压;
  • 保证同一 URL 不会被重复下载;
  • 保证无效(bogus)URL 不会被反复重试;
  • 保证主线程永不被阻塞;
  • 基于 GCD 与 ARC;
  • Arm64 架构支持。

其中“同一 URL 不重复下载”与“无效 URL 不反复重试”两个保证,可以在 SDWebImageManager.m 的downloadImageWithURL:options:progress:completed:实现中看到:框架内部维护failedURLs集合,一旦某 URL 下载失败即被加入黑名单,后续请求除非显式传入SDWebImageRetryFailed选项,否则直接返回缓存不存在的错误;这就是“bogus URLs won't be retried again and again”的源码依据。

版本与系统兼容性

README 明确指出:3.8 版本需要 iOS 7 或更高版本(因为使用了 NSURLSession);3.0 至 3.7 需要 iOS 5.1.1;如需支持 iOS 5.0 以下,只能回退到最后的 2.0 兼容分支。当前仓库实际锁定版本为 3.8.1(见 app/Podfile 的pod 'SDWebImage', :git => "https://github.com/rs/SDWebImage.git", :tag => '3.8.1',以及 app/Podfile.lock 中记录的SDWebImage (3.8.1)),且 JASONETTE-iOS 的 Podfile 声明的部署平台为platform :ios, '8.0'并启用了use_frameworks!,完全满足 3.8 的 iOS 7+ 前提。

SDWebImage 在 JASONETTE-iOS 中的真实落地场景

在深入 API 之前,先看这个仓库里 SDWebImage 的实际使用面——这是本文档主题在当前项目中最直接的印证。通过搜索SDWebImageManager、sd_setImageWithURL:等符号,可以确认以下调用点:

  • JasonImageComponent.m:通过[SDWebImageManager sharedManager].imageDownloader拿到全局下载器,为图片请求动态设置 HTTP Header(来自 Jasonette 的 session 配置与 JSON 中声明的header),再调用sd_setImageWithURL:placeholderImage:completed:加载远程图片;本地file://图片则用[UIImage sd_animatedGIFWithData:]处理动画 GIF。
  • Jason.m:导航栏左右菜单的图片按钮,直接使用SDWebImageManager sharedManager的downloadImageWithURL:options:progress:completed:下载,并在主线程回调中设置按钮图片。
  • JasonAudioAction.m:音频流播放时下载封面图,生成MPMediaItemArtwork写入锁屏播放信息中心。
  • JasonViewController.m、JasonLayer.m、JasonHorizontalSection.m:单元格背景图、layer 背景图等均通过sd_setImageWithURL:异步加载。
  • Jason.h、JasonImageComponent.h、JasonViewController.h、JasonAudioAction.h、JasonUtilAction.h、JasonHorizontalSection.h 统一通过#import <SDWebImage/UIImageView+WebCache.h>引入头文件。

由此可见,SDWebImage 是 JASONETTE-iOS 所有远程图片链路的统一底层,本文后面的 API 讲解也全部对应这些真实调用。

核心用法一:UIImageView+WebCache 分类与 UITableView

最小接入示例

官方推荐的最简单用法,是导入UIImageView+WebCache.h头文件后,在tableView:cellForRowAtIndexPath:中调用sd_setImageWithURL:placeholderImage:,从异步下载到缓存管理全部自动完成:

#import <SDWebImage/UIImageView+WebCache.h> - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *MyIdentifier = @"MyIdentifier"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease]; } // 使用 sd_setImageWithURL: 方法加载网络图片 [cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"]]; cell.textLabel.text = @"My Text"; return cell; }

说明:示例中的autorelease是早期 MRC 时代的写法,当前仓库所有代码均运行在 ARC 环境下,实际工程中直接创建 cell 即可,无需手动管理内存。

完整方法族

UIImageView+WebCache.h 中定义了从简到繁的一整套方法,可按需选择:

方法说明
sd_imageURL获取当前 imageView 正在加载/已加载的图片 URL
sd_setImageWithURL:最简形式,仅指定 URL,异步下载并缓存
sd_setImageWithURL:placeholderImage:指定占位图,下载完成前显示
sd_setImageWithURL:placeholderImage:options:追加SDWebImageOptions行为选项
sd_setImageWithURL:completed:带完成回调(SDWebImageCompletionBlock)
sd_setImageWithURL:placeholderImage:completed:占位图 + 完成回调
sd_setImageWithURL:placeholderImage:options:completed:选项 + 完成回调
sd_setImageWithURL:placeholderImage:options:progress:completed:完整形态:进度回调 + 完成回调
sd_setImageWithPreviousCachedImageWithURL:...优先展示上一次缓存图片,同时尝试刷新
sd_setAnimationImagesWithURLs:用一组 URL 做帧动画循环
sd_cancelCurrentImageLoad取消当前下载
setShowActivityIndicatorView:/setIndicatorStyle:加载时显示系统菊花指示器

其中完成回调SDWebImageCompletionBlock的签名为(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL)——第三个参数cacheType用于判断图片来自网络(SDImageCacheTypeNone)、磁盘缓存(SDImageCacheTypeDisk)还是内存缓存(SDImageCacheTypeMemory),枚举定义见 SDImageCache.h。

注意:如果图片请求在完成前被取消(例如 cell 复用导致旧请求被新请求取代),成功与失败回调都不会被调用——这是官方文档明确说明的行为,也是 UITableView 快速滚动时避免错乱的关键保证。

核心用法二:使用 blocks 感知进度与结果

在下载进度与最终结果都需要感知的场景下,使用带 block 的重载方法。进度回调会在下载过程中反复触发(携带receivedSize与expectedSize),完成回调则在成功或失败时各触发一次:

[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { // ... 完成后的处理代码 }];

该模式在 JASONETTE-iOS 中也被反复使用。以 JasonImageComponent.m 为例:完成回调中把下载图片的实际尺寸写入JasonComponentFactory.imageLoaded,供后续按比例推算高度使用;若 JSON 样式声明了style.color,还会用imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate配合setTintColor:将图片染成指定颜色。

核心用法三:SDWebImageManager——下载器与缓存的调度中枢

直接使用 Manager

SDWebImageManager是UIImageView+WebCache分类背后的核心类,负责把异步下载器(SDWebImageDownloader)与图片缓存(SDImageCache)串联起来(SDWebImageManager.h 中的定义)。在非UIView场景(例如纯 Cocoa 环境、需要手动控制图片用途的场景)下可直接使用:

SDWebImageManager *manager = [SDWebImageManager sharedManager]; [manager downloadImageWithURL:imageURL options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) { // 进度跟踪代码 } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { if (image) { // 使用图片 } }];

注意 Manager 版完成回调比分类版多一个finished参数:当启用SDWebImageProgressiveDownload渐进式下载时,finished == NO的回调会携带半成品图片反复触发,最后一次下载完毕才以finished == YES返回完整图片。

下载流程源码解析

在 SDWebImageManager.m 中可以看到完整调度逻辑:

  1. 参数防御:URL 传成 NSString 会被自动转为 NSURL;传 NSNull 等非法对象则置为 nil(防止崩溃);
  2. 黑名单检查:URL 为空,或(未指定SDWebImageRetryFailed且 URL 在failedURLs中)时直接回调错误,不发请求;
  3. 生成缓存 key(cacheKeyForURL:,默认返回url.absoluteString,若设置了cacheKeyFilter则走过滤器);
  4. 先查缓存(queryDiskCacheForKey:),命中则直接回调缓存图片;未命中或带SDWebImageRefreshCached时再发起下载;
  5. 把 Manager 选项逐位映射为SDWebImageDownloaderOptions下发给下载器。

Manager 还提供cachedImageExistsForURL:/diskImageExistsForURL:(同步与异步两种版本)、cacheKeyForURL:、saveImageToCache:forURL:、cancelAll、isRunning等辅助 API,方便业务侧自行探测缓存状态。

SDWebImageOptions 选项全解

SDWebImageManager通过位掩码SDWebImageOptions控制单次请求行为(定义见 SDWebImageManager.h):

选项位行为
SDWebImageRetryFailed1 << 0关闭 URL 黑名单,允许对失败 URL 重试
SDWebImageLowPriority1 << 1下载延迟到 UI 交互结束后(如滚动减速期)再开始
SDWebImageCacheMemoryOnly1 << 2禁止磁盘缓存,仅写内存缓存
SDWebImageProgressiveDownload1 << 3渐进式下载,边下边显示(类似浏览器)
SDWebImageRefreshCached1 << 4即使命中缓存也尊重 HTTP 缓存控制头并尝试刷新,见下文“图片刷新”
SDWebImageContinueInBackground1 << 5App 退后台后继续下载(iOS 4+)
SDWebImageHandleCookies1 << 6请求携带NSHTTPCookieStore中的 Cookie
SDWebImageAllowInvalidSSLCertificates1 << 7允许不受信任的 SSL 证书(仅测试用)
SDWebImageHighPriority1 << 8将请求提到队列最前
SDWebImageDelayPlaceholder1 << 9延迟加载占位图,直到图片加载完成
SDWebImageTransformAnimatedImage1 << 10对动画图也执行变换代理方法
SDWebImageAvoidAutoSetImage1 << 11不自动设置到 imageView,交由业务在回调中手动设置

核心用法四:独立使用异步下载器 SDWebImageDownloader

如果不想要缓存,只使用下载部分,可以脱离 Manager 直接操作单例下载器(SDWebImageDownloader.h):

SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader]; [downloader downloadImageWithURL:imageURL options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) { // 进度跟踪代码 } completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) { if (image && finished) { // 使用图片 } }];

下载器版完成回调额外返回原始NSData,便于自行处理原始数据(例如判断格式)。下载器的关键可配置属性(结合 SDWebImageDownloader.m 源码确认默认值):

  • maxConcurrentDownloads:最大并发下载数,默认 6;
  • downloadTimeout:请求超时秒数,默认 15.0,并通过NSURLSessionConfiguration.timeoutIntervalForRequest生效;
  • executionOrder:执行顺序,默认SDWebImageDownloaderFIFOExecutionOrder(先进先出),可选SDWebImageDownloaderLIFOExecutionOrder(后进先出,栈式);
  • headersFilter:每次下载请求前对 Header 做统一改写的 block;
  • setValue:forHTTPHeaderField::为所有下载请求追加固定 HTTP Header(JASONETTE-iOS 正是用此 API 注入鉴权头,见 JasonImageComponent.m);
  • urlCredential/username/password:请求凭据;
  • shouldDecompressImages:下载后是否后台解压,默认 YES;
  • setOperationClass::替换默认的SDWebImageDownloaderOperation实现类。

下载器还会通过SDWebImageDownloadStartNotification/SDWebImageDownloadStopNotification发出开始/停止通知,并支持setSuspended:挂起队列与cancelAllDownloads取消全部任务。

核心用法五:独立使用异步缓存 SDImageCache

SDImageCache维护一块内存缓存和一块可选的磁盘缓存,磁盘写入是异步的,不会给 UI 增加额外延迟(SDImageCache.h)。框架提供sharedImageCache单例,也支持用自定义命名空间创建独立实例,从而隔离缓存域:

SDImageCache *imageCache = [[SDImageCache alloc] initWithNamespace:@"myNamespace"]; [imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image) { // image 非 nil 表示缓存命中 }];

查询与写入

  • 查询用queryDiskCacheForKey:done:;若返回 nil,说明缓存未命中,此时应由业务方负责生成并写入图片。缓存 key 是应用内唯一的图片标识,通常就是图片的绝对 URL。
  • 默认情况下,内存未命中会自动继续查磁盘缓存;若只想查内存,用imageFromMemoryCacheForKey:(同步方法)。
  • 写入用storeImage:forKey:,默认同时写入内存与磁盘(磁盘异步);若只想写内存,使用storeImage:forKey:toDisk:并将第三个参数传负数。更精细的写入接口storeImage:recalculateFromImage:imageData:forKey:toDisk:允许直接保存服务端原始imageData,避免二次压缩导致的画质损失与 CPU 开销。

缓存策略与自动清理

从 SDImageCache.m 的初始化实现可确认以下默认行为:

  • 磁盘缓存目录规则为com.hackemist.SDWebImageCache.+ 命名空间,位于默认 Caches 目录下;
  • maxCacheAge默认 7 天(kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7,对应 README 中“自动缓存过期处理”的默认值);磁盘清理通过cleanDisk系列方法删除过期文件;
  • 内存缓存基于AutoPurgeCache(NSCache 子类),收到UIApplicationDidReceiveMemoryWarningNotification时自动clearMemory;App 进入后台或即将终止时自动执行cleanDisk(背景清理);
  • shouldDecompressImages(默认 YES)控制下载图片是否后台解压——解压能提升渲染性能但更耗内存,若因内存过大崩溃可关闭;
  • 还有maxMemoryCost、maxMemoryCountLimit、maxCacheSize等上限参数,以及addReadOnlyCachePath:支持将预置图片目录作为只读缓存路径(适合内置离线图片包)。

清理类方法包括clearMemory(清内存)、clearDisk/clearDiskOnCompletion:(清磁盘)、cleanDisk/cleanDiskWithCompletionBlock:(仅清过期)、getSize/getDiskCount/calculateSizeWithCompletionBlock:(统计占用)。

核心用法六:缓存 key 过滤器

某些场景下图片 URL 带有动态部分(例如用于访问控制的 query 参数),直接用 URL 当缓存 key 会导致同一张图反复缓存、缓存命中率下降。SDWebImageManager提供cacheKeyFilterblock,输入NSURL、输出自定义的缓存 keyNSString。

以下示例在 AppDelegate 中设置过滤器,在生成缓存 key 前剔除 URL 的 query-string:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { SDWebImageManager.sharedManager.cacheKeyFilter = ^(NSURL *url) { url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path]; return [url absoluteString]; }; // 其他初始化代码... return YES; }

其生效位置在 SDWebImageManager.m:cacheKeyForURL:优先调用self.cacheKeyFilter(url),未设置时才回退为[url absoluteString]。

常见问题与踩坑指南

动态图片尺寸导致 UITableViewCell 变形

UITableView 会以第一次设置到 cell 上的图片尺寸来决定 cell 高度。如果远程图片与占位图尺寸不一致,可能出现奇怪的拉伸/变形。官方给出的缓解思路是让 cell 内的图片保持固定宽度(相关文章在 README 中以外部链接形式给出)。JASONETTE-iOS 对这类问题的处理更彻底:它在 JasonImageComponent.m 中根据已加载图片的真实宽高比,在布局阶段自动补齐缺失的height或width,从而保持 cell 尺寸稳定。

图片刷新:SDWebImageRefreshCached

SDWebImage 默认缓存策略非常激进:忽略服务端返回的所有缓存控制头,对图片不做时间限制地长期缓存。这意味着图片 URL 必须是静态的(指向永不变化的资源);若图片内容发生变化,URL 的对应部分也应同步变化。对于无法控制服务端 URL 的场景(典型如 Facebook 头像这类 URL 不变但内容更新的接口),使用SDWebImageRefreshCached选项即可——代价是轻微的性能损失,但会尊重 HTTP 缓存控制头:

[imageView sd_setImageWithURL:[NSURL URLWithString:@"https://graph.facebook.com/olivier.poitrey/picture"] placeholderImage:[UIImage imageNamed:@"avatar-placeholder.png"] options:SDWebImageRefreshCached];

该选项的源码行为见 SDWebImageManager.m:若命中缓存,会先回调一次缓存图片,再发起网络刷新,最后以最终图片回调一次(完成回调触发两次);同时SDWebImageRefreshCached会被映射为下载器层的SDWebImageDownloaderUseNSURLCache,让磁盘缓存交由 NSURLCache 处理。

添加加载进度指示器

README 指向了一个独立的第三方分类(UIActivityIndicator-for-SDWebImage)。实际上,当前版本已内置此能力:setShowActivityIndicatorView:YES配合setIndicatorStyle:即可显示系统菊花。JASONETTE-iOS 正是这样用的(JasonImageComponent.m 中设置灰色指示器)。

安装方式详解

官方文档提供了三种接入方式,结合仓库现状分别说明。

方式一:CocoaPods(当前仓库采用的方式)

platform :ios, '7.0'时写入:

platform :ios, '7.0' pod 'SDWebImage', '~>3.8'

若使用 Swift,需添加use_frameworks!并把平台提升到 iOS 8+:

platform :ios, '8.0' use_frameworks!

当前仓库的 app/Podfile 正是如此:平台声明为ios, '8.0'、开启use_frameworks!,并将 SDWebImage 固定为 git 仓库的 3.8.1 tag。若仓库网络不可用,也可以直接在 app/Pods/SDWebImage 目录中使用已集成的 3.8.1 完整源码。

Subspecs:3.8 提供三个子模块——Core(默认,含核心功能)、MapKit、WebP。默认安装只包含Core,需要 WebP 时显式声明:

pod 'SDWebImage/WebP'

(对应源码中的条件编译:SDWebImageDownloader 仅在定义了SD_WEBP时才把Accept: image/webp,image/*;q=0.8加入默认请求头,见 SDWebImageDownloader.m。)

方式二:Carthage(iOS 8+)

在 Cartfile 中声明:

github "rs/SDWebImage"

集成后,Swift 导入方式视安装途径而定:经 CocoaPods 安装用import SDWebImage,手动安装则import WebImage;Objective-C 使用@import WebImage;。

方式三:手动集成

  • 克隆仓库获取全部文件;
  • 下载/解压框架后,在工程导航器中右键选择 "Add Files to "Your Project"",选中SDWebImage.framework,并勾选 "Copy items into destination group's folder (if needed)";
  • 在 target 的 "Build Phases" → "Link Binary With Libraries" 中添加ImageIO.framework(渐进式下载功能依赖它);
  • 在 "Build Settings" → "Other Linker Flags" 中加入-ObjC;
  • 若-ObjC与扩展了可选库的框架(如 Parse、RestKit、opencv2)发生编译冲突,改用:
    -force_load SDWebImage.framework/Versions/Current/SDWebImage
  • 使用 CocoaPods 且存在上述冲突时,改用:
    -force_load $(TARGET_BUILD_DIR)/libPods.a

    并追加:

    $(inherited)
  • 在需要使用的源文件中导入:
    #import <SDWebImage/UIImageView+WebCache.h>

完成上述步骤后工程即可正常编译。

官方路线图:未来的增强方向

README 末尾列出了作者规划的下一步改进:

  • 用 LRU 内存缓存清理替代“收到内存警告即整体清空”的粗暴策略。

从 SDImageCache.m 可以看到,3.8.1 的内存缓存处理仍是“收到UIApplicationDidReceiveMemoryWarningNotification时调用clearMemory整体清空”,因此该 Roadmap 项在本文档所对应版本中尚未落地,读者如需 LRU 语义可关注后续版本或自行在AutoPurgeCache之上扩展。

许可证

SDWebImage 全部源码以 MIT 许可证发布,仓库内对应文件为 app/Pods/SDWebImage/LICENSE。在 JASONETTE-iOS 中使用(含app/Pods/SDWebImage/SDWebImage目录下的全部实现)即受此许可证约束。

小结

SDWebImage 3.8 用三个层次清晰的组件(UIImageView+WebCache分类、SDWebImageManager调度器、SDImageCache+SDWebImageDownloader基础设施)解决了 iOS 远程图片加载的全部痛点:异步不阻塞主线程、内存/磁盘两级缓存、URL 去重与失败黑名单、GIF/WebP 支持、后台解压与自动过期清理。在 JASONETTE-iOS 中,它承担着从列表缩略图、导航菜单图标、页面背景到音频封面的一切远程图片任务,其仓库内完整源码与各 Jasonette 组件 中的调用点,是学习这套框架最佳的本土化教材。

  • 移动开发
  • 前端

【免费下载链接】JASONETTE-iOS

📡 Native App over HTTP, on iOS

项目地址:https://gitcode.com/gh_mirrors/ja/JASONETTE-iOS
点击查看免费下载

相关推荐

上一篇:使用 Lightning Fabric 加速 DCGAN 人脸生成:从原始 PyTorch 到零样板代码的分布式训练迁移
下一篇:CANN/runtime:查看Ascend RC日志

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进