☰
Nuke 10 迁移指南:从 Nuke 9.x 平滑升级 Image Loading System 的完整实战手册
2026/9/25 3:23:49 网站建设 项目流程
  • 移动开发
  • 图像处理

【免费下载链接】Nuke

Image loading system

项目地址:https://gitcode.com/gh_mirrors/nu/Nuke
点击查看免费下载

本文以 Nuke 官方 Nuke 10 Migration Guide 为骨架,面向正在从 Nuke 9.x 升级到 Nuke 10 的 Swift 开发者,逐一拆解loadImage()签名、磁盘缓存策略、ImagePipeline.Cache统一缓存入口、ImageRequest.Options、ImagePipelineDelegate等关键变更,并结合当前仓库源码(Sources/Nuke/目录)讲解每个新 API 的底层实现原理与推荐用法。读完本文,你将能够不动摇业务逻辑地完成迁移,并掌握 Nuke 10 新的缓存体系与请求选项的正确姿势。

迁移前的准备:最低系统要求与整体思路

升级前请确认工程满足以下基线(与当前仓库 Package.swift 声明的平台兼容范围一致):

  • iOS 11.0、tvOS 11.0、macOS 10.13、watchOS 4.0
  • Xcode 12.0
  • Swift 5.3

Nuke 10 引入大量新特性、细节打磨与性能优化,确实存在一些破坏性变更(breaking changes)与弃用(deprecations)。好消息是:编译器会指引你完成绝大部分改动——被移除的 API 在 Sources/Nuke/Pipeline/Deprecated.swift 中保留了带renamed:标注的不可用桩(unavailable stubs),Xcode 会直接给出 fix-it 建议。因此多数用户甚至不需要这份指南就能完成编译修复;本指南的价值在于帮你理解变更背后的设计意图,避免“编译通过但行为变了”的隐性坑。

想了解 Nuke 10 的全部新特性,可查阅仓库的 CHANGELOG.md 与各版本迁移指南(Documentation/Migrations)。

loadImage() 签名变更:completion 回调变为必填

Nuke 10 中,loadImage(with:)的 completion 回调从可选变为必填:

// Before (Nuke 9) pipeline.loadImage(with: request) // After (Nuke 10) pipeline.loadImage(with: request) { _ in }

如果之前依赖“不传回调、只触发加载”的用法,迁移时直接补一个空闭包即可。从源码看,当前版本的回调 API 均标注为 soft-deprecated(见 Deprecated.swift),新版推荐使用async/await或imageTask(with:),但迁移到 Nuke 10 的第一阶段只需补齐闭包参数即可通过编译。

磁盘缓存策略:从 DataCacheOptions.storedItems 到 DataCachePolicy

Nuke 9 中通过configuration.dataCacheOptions.storedItems控制磁盘缓存内容,Nuke 10 将其替换为全新的DataCachePolicy枚举。三种旧配置的等价迁移如下:

var configuration = ImagePipeline.Configuration() // Before (Nuke 9):只缓存最终处理后的图片 configuration.dataCacheOptions.storedItems = [.finalImage] // After (Nuke 10):编码并存储处理后的图片 configuration.dataCachePolicy = .storeEncodedImages // Before (Nuke 9):只缓存原始图片数据 configuration.dataCacheOptions.storedItems = [.originalImageData] // After (Nuke 10) configuration.dataCachePolicy = .storeOriginalData // Before (Nuke 9):两者都缓存 configuration.dataCacheOptions.storedItems = [.finalImage, .originalImageData] // After (Nuke 10) configuration.dataCachePolicy = .storeAll

如果上述策略都不完全贴合你的需求,可以选用新的.automatic策略,其行为是:

  • 带处理器的请求(如ImageProcessors.Resize):编码并存储处理后的图片;
  • 不带处理器的请求:存储原始图片数据。

这一策略的语义在源码中有精确说明(ImagePipeline+Configuration.swift):它只在带处理器时存储处理图、无处理器时存储原图,对file://、data://等本地资源则只存储处理图。值得注意的是,.storeEncodedImages策略下,loadData(with:)这类只加载数据、不进行解码的 API 不会写入磁盘缓存——因为根本没有解码后的图片可供编码(见 DataCachePolicy 源码注释)。

从源码可以看出,DataCachePolicy的默认值是.storeOriginalData(ImagePipeline+Configuration.swift),迁移后如不显式设置,行为会与 Nuke 9 的默认有所不同,建议按业务需求显式声明。

磁盘缓存配置简化:withDataCache 与 withURLCache

Nuke 9 中启用磁盘缓存需要手动组装DataLoader与DataCache,样板代码冗长:

// Before (Nuke 9) let dataLoader: DataLoader = { let config = URLSessionConfiguration.default config.urlCache = nil return DataLoader(configuration: config) }() var config = ImagePipeline.Configuration() config.dataLoader = dataLoader config.dataCache = try? DataCache(name: "com.github.kean.Nuke.DataCache")

Nuke 10 内置了两个开箱即用的预置配置(ImagePipeline+Configuration.swift):

// After (Nuke 10):激进磁盘缓存(DataCache) let config = ImagePipeline.Configuration.withDataCache // 或者:HTTP 磁盘缓存(URLCache,默认配置) let config = ImagePipeline.Configuration.withURLCache

这两个预置配置的源码实现值得展开说明:

  • withURLCache:即Configuration()默认值,使用系统URLCache(150 MB 上限)做 HTTP 磁盘缓存,配合ImageCache.shared内存缓存;
  • withDataCache:内部正是将 Nuke 9 手写的样板代码封装成工厂方法——创建urlCache = nil的DataLoader,再以默认名称"com.github.kean.Nuke.DataCache"、默认 150 MB 上限创建DataCache(见 withDataCache(name:sizeLimit:) 实现)。它接收name与sizeLimit两个可定制参数,迁移时可以按需调整缓存名称与容量。

另外注意Configuration是一个结构体,但其内部的TaskQueue是类(class)实例,复制配置会共享队列而非深拷贝(见 Configuration 文档注释),修改共享管线配置时需留意这一语义。

统一缓存入口:ImagePipeline.Cache

Nuke 9 时期访问缓存的方式比较割裂:读内存缓存用pipeline.cachedImage(for:),读磁盘缓存要手动构造 key 再调pipeline.dataCache.cachedData(for:)。Nuke 10 引入ImagePipeline.Cache结构体(ImagePipeline+Cache.swift),提供跨所有缓存层的统一读写删 API,同时保持了分层控制能力。

内存缓存的读写

// Before (Nuke 9)(只读) let image = pipeline.cachedImage(for: request) // After (Nuke 10)(可读写) let image = pipeline.cache[request] pipeline.cache[request] = image pipeline.cache[request] = nil

下标操作的实现(ImagePipeline+Cache.swift)会在 setter 收到nil时执行内存缓存移除,收到图片时执行存储——迁移时无需区分读写路径。

磁盘缓存数据的读写

// Before (Nuke 9) let request = ImageRequest(url: URL(string: "https://example.com/image.jpeg")!) let key = pipeline.cacheKey(for: request, item: .originalImageData) let data = pipeline.dataCache.cachedData(for: key) // After (Nuke 10) let request = ImageRequest(url: URL(string: "https://example.com/image.jpeg")) let data = pipeline.cache.cachedData(for: request)
// New (Nuke 10) let request = ImageRequest(url: URL(string: "https://example.com/image.jpeg")) let cache = pipeline.cache let image = cache.cachedImage(for: request) // caches: [.all] let image = cache.cachedImage(for: request, caches: [.disk]) if cache.containsData(for: request) { // ... } cache.removeAll()

Cache提供了一整套便捷方法(ImagePipeline+Cache.swift):

  • cachedImage(for:caches:):从指定缓存层读取并(磁盘路径下)解码图片;
  • storeCachedImage(_:for:caches:):写入所有缓存层,写入磁盘时会按ImageEncoding编码;
  • removeCachedImage(for:caches:)、containsCachedImage(for:caches:);
  • cachedData(for:)、storeCachedData(_:for:)、containsData(for:)、removeCachedData(for:);
  • removeAll(caches:):清空全部缓存层。

caches参数由CachesOptionSet 提供(ImagePipeline+Cache.swift):.memory、.disk与组合.all。从源码注释可以提炼出两条重要的使用约束:

  1. 磁盘 IO 注意事项:读取磁盘缓存(cachedData(for:)、containsData(for:)、cachedImage(for: caches: [.disk]))涉及磁盘 IO 与数据解码,避免在主线程调用;
  2. 预览图(preview)永不写入磁盘,且只有isStoringPreviewsInMemoryCache(默认true)开启时才会进内存缓存(见 storeCachedImage 源码)。

缓存 Key 的生成与自定义

若仍需直接访问缓存 key,ImagePipeline.Cache同样提供:

// Before (Nuke 9) let request = ImageRequest(url: URL(string: "https://example.com/image.jpeg"), processors: [ImageProcessors.Resize(width: 44)]) let originalDataKey = pipeline.cacheKey(for: request, item: .originalImageData) let processedDataKey = pipeline.cacheKey(for: request, item: .finalImage) // After (Nuke 10) let request = ImageRequest(url: URL(string: "https://example.com/image.jpeg")) let originalDataKey = pipeline.cache.makeDataCacheKey(for: request) let request = ImageRequest(url: URL(string: "https://example.com/image.jpeg"), processors: [ImageProcessors.Resize(width: 44)]) let processedDataKey = pipeline.cache.makeDataCacheKey(for: request)

Nuke 10 新增了内存缓存 key 的获取接口:

// New (Nuke 10) let request = ImageRequest(url: URL(string: "https://example.com/image.jpeg")) let originalDataKey = pipeline.cache.makeImageCacheKey(for: request)

key 的生成逻辑在源码中清晰可见:makeDataCacheKey(for:)以请求的imageID为基底,依次拼接缩略图标识与各处理器标识(ImagePipeline+Cache.swift);makeImageCacheKey(for:)则委托给ImageCacheKey,内部综合了imageID、scale、thumbnail与处理器集合(见 Internal/ImageRequestKeys.swift)。两者都会优先使用delegate.cacheKey(for:pipeline:)返回的自定义 key(见下文 Delegate 章节)。

ImageRequestOptions:重命名为 Options,语义重构

Nuke 10 将ImageRequestOptions重做为ImageRequest.Options(ImageRequest.swift)OptionSet,选项名相近但语义有差异。

缓存相关选项的迁移

var request = ImageRequest(url: URL(string: "https://example.com/image.jpeg")!) // Before (Nuke 9) request.cachePolicy = .reloadIgnoringCachedData request.options.filteredURL = "example.com/image.jpeg" // After (Nuke 10) request.options = [.reloadIgnoringCachedData] request.userInfo[.imageIdKey] = "example.com/image.jpeg"

MemoryCacheOptions也并入同一选项集合:

// Before (Nuke 9) request.options.memoryCacheOptions.isReadAllowed = false // After (Nuke 10) request.options = [.disableMemoryCacheRead] // New (Nuke 10):新增的磁盘缓存开关 request.options = [.disableDiskCache]

完整的Options位标志在 ImageRequest.swift 中定义,共 8 项(部分在后续版本扩展):

选项语义
.disableMemoryCacheReads/.disableMemoryCacheWrites/.disableMemoryCache控制内存缓存读写(后两者可组合)
.disableDiskCacheReads/.disableDiskCacheWrites/.disableDiskCache控制磁盘缓存读写
.reloadIgnoringCachedData等价于同时禁用内存与磁盘缓存读(仅对 Nuke 自身缓存层生效,不影响URLCache,见 源码注释)
.returnCacheDataDontLoad只用缓存数据,无缓存则直接失败
.skipDecompression跳过解码后的位图化(bitmapping),延迟到显示时
.skipDataLoadingQueue绕过dataLoadingQueue立即加载,可提升特定任务优先级

这些选项会被Cache的读写路径逐位检查:例如cachedImageFromMemoryCache在请求含.disableMemoryCacheReads时直接返回nil(ImagePipeline+Cache.swift),磁盘读取同理受.disableDiskCacheReads约束(cachedData(for:) 实现)。

弃用的 cacheKey 与 imageIdKey

旧版cacheKey选项设计不佳:它只对内存缓存生效、职责与filteredURL重叠。Nuke 10 推荐统一使用userInfo[.imageIdKey]来覆写图片标识:

// Before (Nuke 9) request.options.cacheKey = "example.com/image.jpeg" // After (Nuke 10) request.userInfo[.imageIdKey] = "example.com/image.jpeg"

在更现代的 Nuke 版本中,这一概念已演进为ImageRequest.imageID属性(ImageRequest.swift):它默认取 URL 的绝对字符串,可显式覆写,用于剥离 URL 中的临时 query 参数、保持缓存 key 稳定。当前仓库的 Deprecated.swift 仍保留了imageIdKey的不可用桩并提示改用imageID,迁移时优先使用新属性。

更小的内存占用

得益于选项系统的重构,ImageRequest的内存占用从 176 字节降到了48 字节(Nuke 10 官方数据),选项更多、体积更小,这对大量持有请求对象的列表场景(如 feed 流)意义重大。

loadKey 被彻底移除

loadKey在 Nuke 10 中被直接移除,该 API 不再有任何作用。任务合并(task coalescing)依旧默认开启(Configuration.isTaskCoalescingEnabled,默认true,见 ImagePipeline+Configuration.swift),如果遇到合并相关的问题,可以通过Configuration将其关闭,并在 GitHub 上提交 issue。

新增:用 ImagePipelineDelegate 自定义缓存 Key

Nuke 10 新增ImagePipelineDelegate协议,允许按请求粒度动态定制缓存 key(ImagePipeline+Delegate.swift):

// New (Nuke 10) final class YourImagePipelineDelegate: ImagePipelineDelegate { func cacheKey(for request: ImageRequest, pipeline: ImagePipeline) -> String? { request.userInfo["someKey"] as? String // 返回 nil 则使用默认 key } }

该回调返回nil时回退到默认 key 生成逻辑。从实现看,makeDataCacheKey(for:)与makeImageCacheKey(for:)都会在生成前询问 delegate(ImagePipeline+Cache.swift),因此自定义 key 同时作用于内存与磁盘缓存。delegate 的默认实现直接返回nil(ImagePipeline+Delegate.swift)。

在后续版本中该协议已更名为ImagePipeline.Delegate(当前仓库源码中即为此名,ImagePipelineDelegate作为renamed:别名保留在 Deprecated.swift),cacheKey(for:pipeline:)的签名与语义保持一致,迁移时按新命名书写即可。

可选请求:ImageView 扩展的 request 现在为可选

Nuke 10 中,图片视图扩展(Nuke.loadImage(with:into:))接受可选的URL/request:

// Before (Nuke 9) if let url = URL(string: string) { Nuke.loadImage(with: url, into: imageView) } else { imageView.image = failureImagePlaceholder } // After (Nuke 10) Nuke.loadImage(with: URL(string: string), into: imageView)

当请求为nil时,按失败场景处理——会展示failureImage占位图并走失败回调,无需再手动分支。这一语义在当前源码中仍有体现:loadImage(with:into:)将nil直接传递给控制器,由控制器按失败流程处理(Sources/NukeUI/ImageViewExtensions.swift)。

扩展 ImageRequestConvertible:支持 String 字面量

ImageRequestConvertible协议现在支持String:

// Before (Nuke 9) pipeline.loadImage(with: URL(string: "https://example.com/image.jpeg")!) { _ in } // New (Nuke 10) pipeline.loadImage(with: "https://example.com/image.jpeg") { _ in }

URL依旧照常可用。其底层实现是ImageRequest遵循ExpressibleByStringLiteral(ImageRequest.swift),字符串会被转换为ImageRequest(url: URL(string: value)),无需手工强制解包。

动画图片:从 associated object 到 ImageContainer.data

Nuke 9 时代,动画图片数据通过configuration.isAnimatedImageDataEnabled = true开启、经response.image.animatedImageData(ObjC associated object)获取。Nuke 10 将其整合进ImageContainer:

// Before (Nuke 9) configuration.isAnimatedImageDataEnabled = true let data = response.image.animatedImageData // ObjC associated object // After (Nuke 10) let data = response.container.data // GIF 等动画格式自动附带

当前ImageContainer(ImageContainer.swift)的data字段语义为:默认解码器识别为动画的图片(GIF、APNG、动画 WebP、HEIC/AVIF 序列)会自动附带原始数据,因为 Image I/O 只会解码动画的第一帧,NukeUI或你自己的渲染引擎需要这份数据来播放。它仅在需要时附加,处理(processing)图片后会随之丢弃。ImageDisplaying协议(Sources/NukeUI/ImageViewExtensions.swift)的回调中也直接下发整个ImageContainer,渲染方可以自行取用data与animation。

ImagePipelineObserving 并入 ImagePipelineDelegate

ImagePipelineObserving协议在 Nuke 10 中被并入新的ImagePipelineDelegate协议:

// Before (Nuke 9) let pipeline = ImagePipeline() pipeline.observer = MockImagePipelineObserver() class YourImagePipelineObserver: ImagePipelineObserving { func pipeline(_ pipeline: ImagePipeline, imageTask: ImageTask, didReceiveEvent event: ImageTaskEvent) { // ... } }
// After (Nuke 10) let pipeline = ImagePipeline(delegate: MockImagePipelineObserver()) class YourImagePipelineObserver: ImagePipelineDelegate { func pipeline(_ pipeline: ImagePipeline, imageTask: ImageTask, didReceiveEvent event: ImageTaskEvent) { // ... } } ImagePipeline.shared = pipeline // 设置为默认管线

当前源码中,delegate 的任务事件钩子为imageTaskCreated、imageTaskDidStart、imageTask(_:didReceiveEvent:pipeline:)(ImagePipeline+Delegate.swift),事件类型为ImageTask.Event(定义于 ImageTask.swift)。这些事件在ImagePipeline启动任务、处理进度与结束任务时被触发(见 ImagePipeline.swift),可用于诊断与统计。

结语:迁移清单速览

将上文全部变更汇总成一份可直接对照执行的检查清单:

  1. 补齐loadImage(with:)的 completion 回调;
  2. 用DataCachePolicy替换dataCacheOptions.storedItems,按需选择.storeOriginalData/.storeEncodedImages/.storeAll/.automatic;
  3. 用Configuration.withDataCache/withURLCache替换手写的磁盘缓存组装代码;
  4. 统一使用pipeline.cache的读写删接口与makeDataCacheKey(for:)/makeImageCacheKey(for:)替代旧的 cacheKey API;
  5. 将ImageRequestOptions/MemoryCacheOptions迁移到ImageRequest.Options,弃用cacheKey与loadKey;
  6. 如需自定义缓存 key,实现ImagePipelineDelegate.cacheKey(for:pipeline:);
  7. 将ImagePipelineObserving改为 delegate 实现,并通过ImagePipeline(delegate:)注入;
  8. 动画数据改从response.container.data读取;
  9. 图片视图 API 可直接接收可选 URL/request,nil 即失败场景。

Nuke 10 的其余变更可查阅 CHANGELOG.md 与仓库内其余迁移指南(Documentation/Migrations)。升级过程中如有疑问,仓库内丰富的测试(如 Tests/NukeTests/ImagePipelineTests 中的缓存与请求契约测试)是验证迁移后行为是否符合预期的最佳参照。

  • 移动开发
  • 图像处理

【免费下载链接】Nuke

Image loading system

项目地址:https://gitcode.com/gh_mirrors/nu/Nuke
点击查看免费下载

相关推荐

上一篇:Shaka Player 5.1.0终极指南:现代流媒体播放器实战
下一篇:Buefy Carousel实现原理:无缝轮播的动画技巧

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询