版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.12.27 星期四 |
前言
OC对有一个很棒的网络图片加载控件SDWebImage,那么Swift中呢,如果要加载远程网络图片呢?这里接下来几篇就给大家介绍一个Swift中用于加载网络图片的框架 —— Nuke。
概览
首先我们看一下作者
它是强大的图像加载和缓存系统。 它使得将图像加载到视图中的任务非常简单,同时还支持更高要求的应用程序的高级功能。
- 快速LRU内存缓存,本机HTTP磁盘缓存和自定义主动LRU磁盘缓存
- 逐步图像加载(渐进式JPEG和WebP)
- 可恢复的下载,请求优先级,重复数据删除,速率限制等
- Alamofire,WebP,Gifu,FLAnimatedImage扩展
- RxNuke - RxSwift扩展
- 使用Preheat自动prefetching(在iOS 10中已弃用)
Getting Started
-
详细Image Pipeline描述
-
整个部分致力于Performance
-
Contributing和路线图
更多信息可在Documentation目录和完整的API Reference中找到。 当您准备好安装Nuke
时,您可以遵循Installation Guide - 支持所有主要的包管理器。
Quick Start
1. Load Image into Image View
您可以使用一行代码将图像加载到图像视图中。
Nuke.loadImage(with: url, into: imageView)
Nuke将自动加载图像数据,在后台解压缩,将图像存储在内存缓存中并显示它。
要学习
ImagePipeline
,see the dedicated section
当您为视图请求新图像时,先前未完成的请求将被取消,图像将设置为nil
。 视图dealloc
时,请求也会自动取消。
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
...
Nuke.loadImage(with: url, into: cell.imageView)
...
}
2. Placeholders, Transitions and More
使用options
参数(ImageLoadingOptions)
自定义图像的加载和显示方式。 您可以提供占位符,选择其中一个内置过渡(transition)
或提供自定义过渡。 使用过渡时,请注意UIKit可能会保留对图像的引用,以防止因为长动画或一次加载多个过渡而被移除。
Nuke.loadImage(
with: url,
options: ImageLoadingOptions(
placeholder: UIImage(named: "placeholder"),
transition: .fadeIn(duration: 0.33)
),
into: imageView
)
有一个很常用的场景:当占位符(或失败的图像)需要以与用于加载的图像不同的content mode
显示时。
let options = ImageLoadingOptions(
placeholder: UIImage(named: "placeholder"),
failureImage: UIImage(named: "failure_image"),
contentModes: .init(
success: .scaleAspectFill,
failure: .center,
placeholder: .center
)
)
Nuke.loadImage(with: url, options: options, into: imageView)
要使应用程序中的所有图像视图共享相同的行为,请修改ImageLoadingOptions.shared
。
如果
ImageLoadingOptions
缺少您需要的功能,请直接使用ImagePipeline
。 如果您认为每个人都可以从此功能中受益,PRs
是不错的选择。
3. Image Requests
每次请求都是ImageRequest
表示的,一次请求可以有URL
或者URLRequest
创建。
var request = ImageRequest(url: url)
// var request = ImageRequest(urlRequest: URLRequest(url: url))
// Change memory cache policy:
request.memoryCacheOptions.isWriteAllowed = false
// Update the request priority:
request.priority = .high
Nuke.loadImage(with: request, into: imageView)
4. Process an Image
使用特殊的ImageRequest初始化器改变图片的尺寸。
// Target size is in pixels.
ImageRequest(url: url, targetSize: CGSize(width: 640, height: 320), contentMode: .aspectFill)
使用processed(key:closure:)
方法执行自定义转换。 以下是使用Toucan创建circular avatar
的方法。
ImageRequest(url: url).process(key: "circularAvatar") {
Toucan(image: $0).maskWithEllipse().image
}
所有这些API都建立在ImageProcessing
协议之上,您也可以使用它来实现自定义处理器。 请记住,ImageProcessing
还需要遵守Equatable
,这有助于Nuke识别内存缓存中的图像。
Nuke中使用Core Image,请参考Core Image Integration Guide
Advanced Usage
1. Image Pipeline
直接使用ImagePipeline加载图像,不用view
let task = ImagePipeline.shared.loadImage(
with: url,
progress: { _, completed, total in
print("progress updated")
},
completion: { response, error in
print("task completed")
}
)
任务可用于监视下载进度,取消请求以及动态更新下载优先级。
task.cancel()
task.setPriority(.high)
有关ImagePipeline的更多信息,请see the dedicated section
2. Configuring Image Pipeline
除了使用共享的ImagePipeline
实例外,您还可以创建自己的实例。
let pipeline = ImagePipeline {
$0.dataLoader = ...
$0.dataLoadingQueue = ...
$0.imageCache = ...
...
}
// When you're done you can make the pipeline a shared one:
ImagePipeline.shared = pipeline
3. Memory Cache
默认Nuke
的ImagePipeline
有两个缓存层。
首先,有一个缓存专门用来存储用来准备显示的处理过的图像。 您可以直接访问此缓存:
// Configure cache
ImageCache.shared.costLimit = 1024 * 1024 * 100 // 100 MB
ImageCache.shared.countLimit = 100
ImageCache.shared.ttl = 120 // Invalidate image after 120 sec
// Read and write images
let request = ImageRequest(url: url)
ImageCache.shared[request] = image
let image = ImageCache.shared[request]
// Clear cache
ImageCache.shared.removeAll()
4. HTTP Disk Cache
为了存储未处理的图像数据,Nuke使用URLCache
实例:
// Configure cache
DataLoader.sharedUrlCache.diskCapacity = 100
DataLoader.sharedUrlCache.memoryCapacity = 0
// Read and write responses
let request = ImageRequest(url: url)
let _ = DataLoader.sharedUrlCache.cachedResponse(for: request.urlRequest)
DataLoader.sharedUrlCache.removeCachedResponse(for: request.urlRequest)
// Clear cache
DataLoader.sharedUrlCache.removeAllCachedResponses()
5. Aggressive Disk Cache
自定义LRU
磁盘缓存可用于快速可靠的主动数据缓存(忽略HTTP cache control)。 您可以使用管道配置启用它。
$0.dataCache = try! DataCache(name: "com.myapp.datacache")
// On Swift 4.1 and lower you'll also need to provide a `FilenameGenerator`.
如果启用了主动磁盘缓存,请确保还禁用本机URL缓存(请参阅DataLoader
),否则最终可能会存储两次相同的映像数据。
DataCache
类型实现公共DataCaching
协议,可用于实现自定义数据缓存。
6. Prefetching Images
预先Prefethcing图像可以减少用户的等待时间。 Nuke
提供了一个ImagePreheater
来做到这一点:
let preheater = ImagePreheater()
preheater.startPreheating(with: urls)
// Cancels all of the preheating tasks created for the given requests.
preheater.stopPreheating(with: urls)
需要权衡,prefetching
会占用用户的数据,并对CPU和内存造成额外压力。 要减少CPU和内存使用量,您可以选择仅将磁盘缓存作为prefetching
目标:
// The preheater with `.diskCache` destination will skip image data decoding
// entirely to reduce CPU and memory usage. It will still load the image data
// and store it in disk caches to be used later.
let preheater = ImagePreheater(destination: .diskCache)
为确保prefetching
请求不会干扰正常请求,最好降低其优先级。
您可以将Nuke
与Preheat库结合使用,该库可自动preheating
UICollectionView
和UITableView
中的内容。 在iOS 10.0上,您可能希望使用iOS提供的新prefetching APIs。
7. Progressive Decoding
要使用渐进式图像加载,您需要启用渐进式解码的管道。
let pipeline = ImagePipeline {
$0.isProgressiveDecodingEnabled = true
}
就是这样,你可以开始观察管道产生的图像。 进度处理程序也可用作渐进式图像处理程序。
let imageView = UIImageView()
let task = ImagePipeline.shared.loadImage(
with: url,
progress: { response, _, _ in
imageView.image = response?.image
},
completion: { response, _ in
imageView.image = response?.image
}
)
请参阅
"Progressive Decoding"
演示,了解渐进式JPEG在实践中的应用。
8. Animated Images
Nuke
使用animatedImageData
属性扩展UIImage
。 如果通过将ImagePipeline.Configuration.isAnimatedImageDataEnabled
设置为true
来启用它,则管道将开始将原始图像数据附加到动画图像(内置解码器现在仅支持GIF)。
在计算缓存项目的成本时,
ImageCache
会将animatedImageData
考虑在内。ImagePipeline
不会将处理器应用于带有动画数据的图像。
没有内置的方法来渲染这些图像,但有两种可用的集成:FLAnimatedImage和Gifu,它们既快速又高效。
GIF
不是传输和显示动画图像的最有效格式。 目前的最佳做法是use short videos instead of GIFs(例如MP4
,WebM
)。 演示项目中有一个PoC
,它使用Nuke
加载,缓存和显示MP4视频。
9. WebP
WebP
支持由Ryo Kosuge构建的Nuke WebP Plugin插件提供。 请按照repo
中的说明进行安装。
10. RxNuke
RxNuke为Nuke添加了RxSwift扩展,并支持许多常见用例:
- Going from low to high resolution
- Loading the first available image
- Showing stale image while validating it
- Load multiple images, display all at once
- Auto retry on failures
- And more...
以下是将go flow log
加载到高分辨率是多么容易的示例:
let pipeline = ImagePipeline.shared
Observable.concat(pipeline.loadImage(with: lowResUrl).orEmpty,
pipeline.loadImage(with: highResUtl).orEmpty)
.subscribe(onNext: { imageView.image = $0 })
.disposed(by: disposeBag)
Image Pipeline
Nuke
的图像管道由大约五个阶段组成,可以使用以下协议进行自定义:
1. Default Image Pipeline
默认图像管道配置如下所示:
ImagePipeline {
// Shared image cache with a `sizeLimit` equal to ~20% of available RAM.
$0.imageCache = ImageCache.shared
// Data loader with a `URLSessionConfiguration.default` but with a
// custom shared URLCache instance:
//
// public static let sharedUrlCache = URLCache(
// memoryCapacity: 0,
// diskCapacity: 150 * 1024 * 1024, // 150 MB
// diskPath: "com.github.kean.Nuke.Cache"
// )
$0.dataLoader = DataLoader()
// Custom disk cache is disabled by default, the native URL cache used
// by a `DataLoader` is used instead.
$0.dataCache = nil
// Each stage is executed on a dedicated queue with has its own limits.
$0.dataLoadingQueue.maxConcurrentOperationCount = 6
$0.imageDecodingQueue.maxConcurrentOperationCount = 1
$0.imageProcessingQueue.maxConcurrentOperationCount = 2
// Combine the requests for the same original image into one.
$0.isDeduplicationEnabled = true
// Progressive decoding is a resource intensive feature so it is
// disabled by default.
$0.isProgressiveDecodingEnabled = false
}
2. Image Pipeline Overview
这是当您调用Nuke.loadImage(with: url, into: imageView
方法时发生的情况。
首先,Nuke
同步检查映像是否在内存缓存中可用(pipeline.configuration.imageCache)
。 如果不是,Nuke调用pipeline.loadImage(with:request)
方法。 管道还检查图像是否在其内存缓存中可用,如果没有,则开始加载它。
在开始加载图像数据之前,管道还会检查是否存在对同一图像的任何现有未完成请求。 如果找到,则不会创建新请求。
默认情况下,使用带有自定义URLCache
实例的URLSession
加载数据(请参阅上面的配置)。 URLCache
支持磁盘缓存,但它需要启用HTTP
缓存。
参考Image Caching Guide了解更多。
加载数据时,管道解码数据(从Data
创建UIImage
对象)。 然后它应用默认图像处理器 - ImageDecompressor
- 在后台强制数据解压缩。 然后将处理后的图像存储在存储器高速缓存中并在完成闭包中返回。
创建
UIImage
对象格式数据时,数据不会立即解码。 它在第一次使用时被解码 - 例如,当您在图像视图中显示图像时。 解码是一项资源密集型操作,如果您在主线程上执行此操作,您可能会看到丢帧,尤其是JPEG
等图像格式。为防止在主线程上发生解码,Nuke会在后台为您执行此操作。 但为了获得更好的性能,建议对图像进行下采样
(downsample)
。 为此,请创建具有目标视图大小的请求:ImageRequest(url: url, targetSize: CGSize(width: 640, height: 320), contentMode: .aspectFill)
警告:target的尺寸是像素级别的。
要了解有关图像的decoding and downsampling
,请参考Image and Graphics Best Practices
3. Data Loading and Caching
内置的DataLoader
类实现DataLoading
协议,并使用URLSession
加载图像数据。 使用URLCache
实例将数据缓存在磁盘上,默认情况下初始化内存容量为0 MB(Nuke
将图像存储在内存中,而不是图像数据),磁盘容量为150 MB。
URLSession
类本身支持data
,file
,ftp
,http
和https URL schemes
。 图像流水线也可以与任何这些schemes
一起使用。
要了解有关图像缓存的请参考Image Caching Guide
要了解有关如何自定义数据加载和缓存的,请参考Third Party Libraries
大多数开发人员要么实现自己的网络层,要么使用第三方框架。 Nuke支持这两种工作流程。 您可以通过实现DataLoading
协议来集成您的自定义网络层。
使用 Alamofire框架实现
DataLoading
协议的可以参考Alamofire Plugin
4. Memory Cache
准备好显示的已处理图像存储在快速内存高速缓存(ImageCache)
中。 它使用 LRU (least recently used)替换算法,并且有一个限制,可以防止它使用超过20%的可用RAM。ImageCache
会自动清除内存警告上的图像,并在应用程序进入后台时删除大部分图像。
5. Resumable Downloads
如果数据任务终止(由于失败或取消)并且图像被部分下载,则下一次加载将从停止的位置恢复。
可恢复的下载要求服务器支持HTTP Range Requests。 Nuke支持两种验证器(ETag
和Last-Modified
)。 默认情况下启用可恢复下载。
默认情况下,可恢复数据存储在高效的内存缓存中。 未来版本可能包括更多自定义。
6. Request Dedupication
默认情况下,ImagePipeline
将对同一图像(但可以是不同的处理器(processors)
)的请求组合到同一任务中。 任务的优先级设置为已注册请求的最高优先级,并在向任务添加或删除请求时进行更新。 只有在所有已注册的请求都被取消时,任务才会被取消
可以使用
ImagePipeline.Configuration
禁用重复数据删除。
Performance
性能是Nuke
的关键差异因素之一。
该框架被调整为尽可能少地在主线程上工作。它使用多种优化技术来实现:减少分配数量,减少动态分派,通过引用类型存储支持一些结构以减少ARC开销等。
Nuke
是完全异步的,在压力下工作得很好。 ImagePipeline
在专用队列上调度其每个阶段。每个队列限制并发任务的数量,即使在队列之间移动时也要尊重请求优先级,并尽快取消工作。在某些负载下,ImagePipeline
还会对请求进行速率限制,以防止底层系统被丢弃。
另一个重要的性能特征是内存使用。 Nuke
使用具有LRU (least recently used)替换算法的自定义内存缓存。它有一个限制,可以防止它使用超过20%的可用RAM。ImageCache
会自动清除内存警告上的图像,并在应用程序进入后台时删除大部分图像。
1. Performance Metrics
在优化性能时,量化是很重要的。 Nuke
在执行每个图像任务期间收集详细的性能指标:
ImagePipeline.shared.didFinishCollectingMetrics = { task, metrics in
print(metrics)
}
(lldb) po metrics
Task Information {
Task ID - 1
Duration - 22:35:16.123 – 22:35:16.475 (0.352s)
Was canceled - false
Is Memory Cache Hit - false
Was Subscribed To Existing Session - false
}
Session Information {
Session ID - 1
Total Duration - 0.351s
Was Canceled - false
}
Timeline {
22:35:16.124 – 22:35:16.475 (0.351s) - Total
------------------------------------
nil – nil (nil) - Check Disk Cache
22:35:16.131 – 22:35:16.410 (0.278s) - Load Data
22:35:16.410 – 22:35:16.468 (0.057s) - Decode
22:35:16.469 – 22:35:16.474 (0.005s) - Process
}
Resumable Data {
Was Resumed - nil
Resumable Data Count - nil
Server Confirmed Resume - nil
}
Extensions
Nuke
提供了多种扩展,其中一些扩展由社区构建。
Name | Description |
---|---|
RxNuke | RxSwift extensions for Nuke with examples of common use cases solved by Rx |
Alamofire | Replace networking layer with Alamofire and combine the power of both frameworks |
WebP | [Community] WebP support, built by Ryo Kosuge |
Gifu | Use Gifu to load and display animated GIFs |
FLAnimatedImage | Use FLAnimatedImage to load and display animated GIFs |
Requirements
iOS 9.0 / watchOS 2.0 / macOS 10.10 / tvOS 9.0
Xcode 9.2 - Xcode 10
Swift 4.0 - Swift 4.2
后记
本篇主要介绍了Nuke这个Swift网络图像加载框架,感兴趣的给个赞或者关注~~~
网友评论