Nuke框架详细解析(一) —— 基本概览(一)

版本记录

版本号 时间
V1.0 2020.10.09 星期五

前言

在我们开发中总有从远程下载图片,OC中有个很成熟的三方框架,大家都知道的SDWebImage,同样Swift中也有类似的三方框架Nuke,接下来几篇我们就一起看一下这个框架。

开始

首先我们看一下该项目的GitHub地址 Nuke。

Nuke提供了一种简单有效的方法来下载和显示应用程序中的图像。 简洁明了的API背后是先进的体系结构,该体系结构具有其独特的功能并提供了几乎无限的自定义可能性。 Nuke的主要功能是性能 - performance。

Fast LRU memory and disk cache · SwiftUI · Smart background decompression · Image processing · Elegant builder API · Resumable downloads · Intelligent deduplication · Request prioritization · Low data mode · Prefetching · Rate limiting · Progressive JPEG, HEIF, WebP, SVG, GIF · Alamofire · Combine · Reactive extensions

Nuke易于学习和使用。 以下是其API和功能的概述:

  • Image View Extensions - UI Extensions · Table View · Placeholders, Transitions · ImageRequest
  • Image Processing - Resize · Circle · RoundedCorners · GaussianBlur · CoreImage · Custom Processors
  • Image Pipeline - Load Image · ImageTask · Customize Image Pipeline · Default Pipeline
  • Caching - LRU Memory Cache · HTTP Disk Cache · Aggressive LRU Disk Cache
  • Advanced Features - Preheat Images · Progressive Decoding
  • Extensions ‣ FetchImage · Builder · Combine · RxNuke · And More

要了解更多信息,请参阅完整的API Reference,并查看存储库中包含的演示项目。 准备安装时,请遵循Installation Guide。 请参阅Requirements,以获取受支持平台的列表。 如果遇到任何问题,请参阅FAQTroubleshooting Guide

要了解有关管道和支持的格式的更多信息,请参见专用指南。

  • Image Formats ‣ Progressive JPEG · HEIF · GIF · SVG · WebP
  • Image Pipeline ‣ Data Loading · Resumable Downloads · Memory Cache · Deduplication · Decompression · Performance

Image View Extensions

用单行代码下载并在image view中显示图像:

Nuke.loadImage(with: url, into: imageView)

Nuke将检查图像是否在内存缓存中,如果存在,将立即显示它。 否则,图像数据将在后台加载,解码,处理和解压缩

请参阅 Image Pipeline Guide以了解如何下载和处理图像。

1. In a Table View

当您为现有视图请求新图像时,Nuke会为重用做好准备,并取消对该视图的所有未完成请求。

func tableView(_ tableView: UITableView, cellForItemAt indexPath: IndexPaths) -> UITableViewCell {
    /* Create a cell ... */
    Nuke.loadImage(with: url, into: cell.imageView)
}

视图deallocated后,关联的请求将自动取消。 要手动取消请求,请调用Nuke.cancelRequest(for:imageView)

2. Placeholders, Transitions, Content Modes, Tint Colors

使用ImageLoadingOptions设置一个placeholder,选择一个内置transitions,或提供一个自定义过渡。

let options = ImageLoadingOptions(
    placeholder: UIImage(named: "placeholder"),
    transition: .fadeIn(duration: 0.33)
)
Nuke.loadImage(with: url, options: options, into: imageView)

您甚至可以自定义content mode或每种图像类型的tint color

let options = ImageLoadingOptions(
    placeholder: UIImage(named: "placeholder"),
    failureImage: UIImage(named: "failureImage"),
    contentModes: .init(success: .scaleAspectFill, failure: .center, placeholder: .center),
    tintColors: .init(success: .green, failure: .red, placeholder: .yellow)
)

如果希望所有图像视图都具有相同的行为,则可以修改ImageLoadingOptions.shared

请记住,图像视图的内置扩展程序旨在使您尽快启动并运行。 如果您想拥有更多控制权或使用某些高级功能(例如动画图像),建议直接在自定义视图中使用ImagePipeline

3. ImageRequest

ImageRequest允许您设置图像处理器,更改请求优先级等:

let request = ImageRequest(
    url: URL(string: "http://..."),
    processors: [ImageProcessors.Resize(size: imageView.bounds.size)],
    priority: .high
)

应用处理器的另一种方法是在ImagePipeline.Configuration上设置默认processors。 这些处理器将应用于管道加载的所有图像。 如果请求中包含非空的处理器数组,则将应用它们。

可通过ImageRequestOptions使用的高级选项。 例如,如果URL包含瞬态查询参数,则可以提供一个filteredURL用作缓存的键。

let request = ImageRequest(
    url: URL(string: "http://example.com/image.jpeg?token=123")!,
    options: ImageRequestOptions(
        filteredURL: "http://example.com/image.jpeg"
    )
)

有更多选项可用,要查看所有选项,请查看内联文档以了解ImageRequestOptions


Image Processing

Nuke具有强大而高效的图像处理基础架构,具有多个内置处理器,您可以在ImageProcessors命名空间中找到它们,例如 ImageProcessors.Resize

此截图和其他屏幕截图来自仓库中包含的演示项目。

1. Resize

要调整图像大小,请使用ImageProcessors.Resize

ImageRequest(url: url, processors: [
    ImageProcessors.Resize(size: imageView.bounds.size)
])

默认情况下,target size以点为单位。 加载图像后,Nuke将缩小图像以填充目标区域,并保持宽高比。 要裁切图像,请将crop设置为true。 有关更多选项,请参见ImageProcessors.Resize文档。

使用可选的Builder软件包获得更简洁的API。

pipeline.image(with: URL(string: "https://")!)
    .resize(width: 320)
    .blur(radius: 10)

2. Circle

将图像的角圆成带有可选边框的圆。

ImageRequest(url: url, processors: [
    ImageProcessors.Circle()
])

3. RoundedCorners

将图像的角圆整到指定的半径。 确保调整图像大小以与显示图像的视图大小完全匹配,以便正确显示边框。

ImageRequest(url: url, processors: [
    ImageProcessors.Circle(radius: 16)
])

4. GaussianBlur

ImageProcessors.GaussianBlur使用Core Image滤镜之一模糊输入图像。

5. CoreImageFilter

使用ImageProcessors.CoreImageFilter应用大量的Core Image filters:

ImageProcessors.CoreImageFilter(name: "CISepiaTone")

6. Custom Processors

对于简单的一次性操作,请使用ImageProcessors.Anonymous创建带闭包的处理器。

定制处理器需要实现ImageProcessing协议。 为了满足基本的图像处理需求,请实现process(_ :)方法并创建一个唯一标识处理器的标识符。 对于没有输入参数的处理器,您可以返回静态字符串。

public protocol ImageProcessing {
    func process(image: UIImage) -> UIImage? // NSImage on macOS
    var identifier: String // get
}

如果您的处理器需要操纵图像元数据(ImageContainer),或者需要通过ImageProcessingContext访问更多信息,那么除了process(_ :)之外,您还可以实现其他方法。

public protocol ImageProcessing {
    func process(_ image container: ImageContainer, context: ImageProcessingContext) -> ImageContainer?
}

除了var identfier:String外,您还可以实现var hashableIdentifier:AnyHashable,以供内存高速缓存使用,因为在这种情况下,字符串操作太慢。 默认情况下,此方法返回identifier字符串。 一种常见的方法是使处理器可Hashable,并从hashableIdentifier返回self


Image Pipeline

Nuke的核心是ImagePipeline类。 直接使用管道来加载图像而不显示它们:

let task = ImagePipeline.shared.loadImage(
    with: url,
    progress: { _, completed, total in
        print("progress updated")
    },
    completion: { result: Result in
        print("task completed")
    }
)

loadImage返回值始终异步调用完成闭包。 若要检查图像是否存储在内存缓存中,请使用pipeline.cachedImage(for:url)

要下载数据而不进行任何昂贵的解码或处理,请使用loadData(with:progress:completion :)

1. ImageTask

启动请求时,管道将返回ImageTask对象,该对象可用于取消操作以及更多操作。

task.cancel()
task.priority = .high

2. Customize Image Pipeline

如果您想构建一个适合您特定需求的系统,那么您将不会失望。 有很多事情需要调整。 您可以设置自定义数据加载器和缓存,配置图像编码器和解码器,更改每个单独阶段的并发操作数,禁用和启用重复数据删除和速率限制等功能。

要了解更多信息,请参见ImagePipeline.Configuration和Image Pipeline Guide的内联文档。

以下是可用于自定义的协议:

  • DataLoading –下载(或返回缓存的)图像数据
  • DataCaching –将图像数据存储在磁盘上
  • ImageDecoding –将数据转换为图像(有关新的实验性解码功能,请参见_ImageDecoding
  • ImageEncoding-将图像转换为数据
  • ImageProcessing –应用图像转换
  • ImageCaching –将图像存储到内存缓存中
Nuke框架详细解析(一) —— 基本概览(一)_第1张图片

整个配置由ImagePipeline.Configuration结构描述。 要使用自定义配置创建管道,请调用ImagePipeline(configuration :)初始值设定项或使用便捷的一种:

let pipeline = ImagePipeline {
    $0.dataLoader = ...
    $0.dataLoadingQueue = ...
    $0.imageCache = ...
    ...
}

然后将新管道设置为默认值:

ImagePipeline.shared = pipeline

3. Default Image Pipeline

默认图像管道使用以下依赖项(dependencies)进行初始化:

// Shared image cache with a size limit of ~20% of available RAM.
imageCache = ImageCache.shared

// Data loader with a default `URLSessionConfiguration` and a custom `URLCache`
// with memory capacity 0, and disk capacity 150 MB.
dataLoader = DataLoader()

// Custom aggressive disk cache is disabled by default.
dataCache = nil

// By default uses the decoder from the global registry and the default encoder.
makeImageDecoder = ImageDecoderRegistry.shared.decoder(for:)
makeImageEncoder = { _ in ImageEncoders.Default() }

管道中的每个操作都在专用队列上运行:

dataLoadingQueue.maxConcurrentOperationCount = 6
dataCachingQueue.maxConcurrentOperationCount = 2
imageDecodingQueue.maxConcurrentOperationCount = 1
imageEncodingQueue.maxConcurrentOperationCount = 1
imageProcessingQueue.maxConcurrentOperationCount = 2
imageDecompressingQueue.maxConcurrentOperationCount = 2

您可以调整以下管道设置列表:

// Automatically decompress images in the background by default.
isDecompressionEnabled = true

// Configure what content to store in the custom disk cache.
dataCacheOptions.storedItems = [.finalImage] // [.originalImageData]

// Avoid doing any duplicated work when loading or processing images.
isDeduplicationEnabled = true

// Rate limit the requests to prevent trashing of the subsystems.
isRateLimiterEnabled = true

// Progressive decoding is an opt-in feature because it is resource intensive.
isProgressiveDecodingEnabled = false

// Don't store progressive previews in memory cache.
isStoringPreviewsInMemoryCache = false

// If the data task is terminated (either because of a failure or a
// cancellation) and the image was partially loaded, the next load will
// resume where it was left off.
isResumableDataEnabled = true

在所有管道之间共享一些全局选项:

// Enable to start using `os_signpost` to monitor the pipeline
// performance using Instruments.
ImagePipeline.Configuration.isSignpostLoggingEnabled = false

Caching

1. LRU 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] = ImageContainer(image: image)
let image = ImageCache.shared[request]

// Clear cache
ImageCache.shared.removeAll()

ImageCache使用LRU算法-在扫描期间,首先删除最近最少使用的条目。

2. HTTP Disk Cache

未处理的图像数据存储在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()

3. Aggressive LRU Disk Cache

如果您不愿意使用HTTP缓存,则可以尝试使用自定义LRU磁盘缓存进行快速可靠的主动数据缓存(忽略 HTTP cache control)。 您可以使用管道配置启用它。

ImagePipeline {
    $0.dataCache = try? DataCache(name: "com.myapp.datacache")

    // Also consider disabling the native HTTP cache, see `DataLoader`.
}

默认情况下,管道仅存储原始图像数据。 要存储已下载和已处理的图像,请将dataCacheOptions.storedItems设置为[.finalImage]。 如果您要存储已处理的内容,例如下采样的图像,或者如果您想将图像转码为更有效的格式,例如HEIF

要节省磁盘空间,请参阅HEIF支持的ImageEncoders.ImageIOImageEncoder.isHEIFPreferred选项。


Advanced Features

1. Image Preheating

预先预取图像可以显着改善应用程序的用户体验。

// Make sure to keep a strong reference to preheater.
let preheater = ImagePreheater()

preheater.startPreheating(with: urls)

// Cancels all of the preheating tasks created for the given requests.
preheater.stopPreheating(with: urls)

要了解有关您可以执行的其他性能优化的更多信息,请参见Performance Guide。

请记住,预取会占用用户的数据,并给CPU和内存带来额外的压力。 为了减少CPU和内存的使用,您可以选择仅选择磁盘缓存作为预取目标:

// 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)

iOS上,您可以将prefetching APIs与ImagePreheater结合使用以自动执行该过程。

2. Progressive Decoding

要启用渐进式图像解码,请将isProgressiveDecodingEnabled配置选项设置为true

let pipeline = ImagePipeline {
    $0.isProgressiveDecodingEnabled = true
    
    // If `true`, the pipeline will store all of the progressively generated previews
    // in the memory cache. All of the previews have `isPreview` flag set to `true`.
    $0.isStoringPreviewsInMemoryCache = true
}

就是这样,管道将自动执行正确的操作,并在进度扫描到达时通过progress闭包进行渐进式扫描:

let imageView = UIImageView()
let task = ImagePipeline.shared.loadImage(
    with: url,
    progress: { response, _, _ in
        if let response = response {
            imageView.image = response.image
        }
    },
    completion: { result in
        // Display the final image
    }
)

Extensions

Nuke有多种扩展:

Name Description
FetchImage SwiftUI integration
ImagePublisher Combine publishers for Nuke
ImageTaskBuilder A fun and convenient way to use Nuke
Alamofire Plugin Replace networking layer with Alamofire and combine the power of both frameworks
RxNuke RxSwift extensions for Nuke with examples of common use cases solved by Rx
WebP Plugin [Community] WebP support, built by Ryo Kosuge
Gifu Plugin Use Gifu to load and display animated GIFs
FLAnimatedImage Plugin Use FLAnimatedImage to load and display animated GIFs
Xamarin NuGet [Community] Makes it possible to use Nuke from Xamarin

1. FetchImage

FetchImage是一个Swift软件包,可轻松使用Nuke下载图像并将其显示在SwiftUI应用中。 有关更多信息,请参见introductory post。

注意:这是一个API预览,将来可能会更改。

public struct ImageView: View {
    @ObservedObject var image: FetchImage

    public var body: some View {
        ZStack {
            Rectangle().fill(Color.gray)
            image.view?
                .resizable()
                .aspectRatio(contentMode: .fill)
        }
    }
}

2. Low Data Mode

FetchImage还通过特殊的初始化程序为低数据模式提供内置支持:

FetchImage(regularUrl: highQualityUrl, lowDataUrl: lowQualityUrl)

3. Builder

觉得默认的API有点无聊吗? 试试ImageTaskBuilder,这是一种使用Nuke的有趣且方便的方法。

ImagePipeline.shared.image(with: URL(string: "https://")!)
    .fill(width: 320)
    .blur(radius: 10)
    .priority(.high)
    .start { result in
        print(result)
    }

// Returns `ImageTask` when started.
let imageView: UIImageView

ImagePipeline.shared.image(with: URL(string: "https://")!)
    .fill(width: imageView.size.width)
    .display(in: imageView)

4. 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

要了解使用此扩展程序可以做什么,请看一下先加载低分辨率图像然后切换到高分辨率有多么容易:

let pipeline = ImagePipeline.shared
Observable.concat(pipeline.loadImage(with: lowResUrl).orEmpty,
                  pipeline.loadImage(with: highResUrl).orEmpty)
    .subscribe(onNext: { imageView.image = $0 })
    .disposed(by: disposeBag)

5. Combine

ImagePublisher为Nuke添加了Combine发行者,并且与RxNuke一样,启用了各种功能强大的用例。


Contribution

Nuke's roadmap在Trello中管理,并且可以公开获得。 如果您想贡献,请随时创建PR

1. Minimum Requirements

Nuke Swift Xcode Platforms
Nuke 9.0 Swift 5.1 Xcode 11.0 iOS 11.0 / watchOS 4.0 / macOS 10.13 / tvOS 11.0
Nuke 8.0 Swift 5.0 Xcode 10.2 iOS 10.0 / watchOS 3.0 / macOS 10.12 / tvOS 10.0

有关旧版本的信息,请参见Installation Guide。

后记

本篇主要讲述了Swift中也有类似的三方框架Nuke,感兴趣的给个赞或者关注~~~

Nuke框架详细解析(一) —— 基本概览(一)_第2张图片

你可能感兴趣的:(Nuke框架详细解析(一) —— 基本概览(一))