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

作者: 刀客传奇 | 来源:发表于2018-12-27 14:39 被阅读43次

    版本记录

    版本号 时间
    V1.0 2018.12.27 星期四

    前言

    OC对有一个很棒的网络图片加载控件SDWebImage,那么Swift中呢,如果要加载远程网络图片呢?这里接下来几篇就给大家介绍一个Swift中用于加载网络图片的框架 —— Nuke

    概览

    首先我们看一下作者

    它是强大的图像加载和缓存系统。 它使得将图像加载到视图中的任务非常简单,同时还支持更高要求的应用程序的高级功能。

    • 快速LRU内存缓存,本机HTTP磁盘缓存和自定义主动LRU磁盘缓存
    • 逐步图像加载(渐进式JPEG和WebP)
    • 可恢复的下载,请求优先级,重复数据删除,速率限制等
    • AlamofireWebPGifuFLAnimatedImage扩展
    • RxNuke - RxSwift扩展
    • 使用Preheat自动prefetching(在iOS 10中已弃用)

    Getting Started

    更多信息可在Documentation目录和完整的API Reference中找到。 当您准备好安装Nuke时,您可以遵循Installation Guide - 支持所有主要的包管理器。


    Quick Start

    1. Load Image into Image View

    您可以使用一行代码将图像加载到图像视图中。

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

    Nuke将自动加载图像数据,在后台解压缩,将图像存储在内存缓存中并显示它。

    要学习ImagePipelinesee 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

    默认NukeImagePipeline有两个缓存层。

    首先,有一个缓存专门用来存储用来准备显示的处理过的图像。 您可以直接访问此缓存:

    // 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请求不会干扰正常请求,最好降低其优先级。

    您可以将NukePreheat库结合使用,该库可自动preheating UICollectionViewUITableView中的内容。 在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不会将处理器应用于带有动画数据的图像。

    没有内置的方法来渲染这些图像,但有两种可用的集成:FLAnimatedImageGifu,它们既快速又高效。

    GIF不是传输和显示动画图像的最有效格式。 目前的最佳做法是use short videos instead of GIFs(例如MP4WebM)。 演示项目中有一个PoC,它使用Nuke加载,缓存和显示MP4视频。

    9. WebP

    WebP支持由Ryo Kosuge构建的Nuke WebP Plugin插件提供。 请按照repo中的说明进行安装。

    10. RxNuke

    RxNuke为Nuke添加了RxSwift扩展,并支持许多常见用例:

    以下是将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类本身支持datafileftphttphttps 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支持两种验证器(ETagLast-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网络图像加载框架,感兴趣的给个赞或者关注~~~

    相关文章

      网友评论

        本文标题:Nuke框架详细解析(一) —— 基本概览(一)

        本文链接:https://www.haomeiwen.com/subject/qsgllqtx.html