[翻译] Fast Image Cache

https://github.com/path/FastImageCache

 

Fast Image Cache is an efficient, persistent, and—above all—fast way to store and retrieve images in your iOS application. Part of any good iOS application's user experience is fast, smooth scrolling, and Fast Image Cache helps make this easier.

Fast Image Cache 是一个高效率、稳定持久的存储、恢复大量图片的开源库。在滑动浏览很多图片的时候,很多开发的很好的应用的用户体验是十分流畅平滑的,在这里,你可以不用求人了,Fast Image Cache 会让这个变得很容易。

A significant burden on performance for graphics-rich applications like Path is image loading. The traditional method of loading individual images from disk is just too slow, especially while scrolling. Fast Image Cache was created specifically to solve this problem.

为了处理复杂的动画效果提升用户体验,本身系统开销就大,此时再加载大量图片会成为一个很高的负担。传统的方式,从磁盘中单个读取图片来加载太慢了,尤其是在用户滑动的时候。看看 Path 应用是怎么做到的。Fast Image Cache 就是被设计来解决这个问题的。

What Fast Image Cache Does(看看 Fast Image Cache 做了些什么

  • Stores images of similar sizes and styles together
  • Persists image data to disk
  • Returns images to the user significantly faster than traditional methods
  • Automatically manages cache expiry based on recency of usage
  • Utilizes a model-based approach for storing and retrieving images
  • Allows images to be processed on a per-model basis before being stored into the cache
  • 将类似的 size 、style 的图片存储在一起
  • 将图片固化到磁盘
  • 相对于传统方法,返回图片给用户相当的快
  • 基于使用率自动管理缓存
  • 基于 model 的方式来存储和恢复图片
  • 在存储到缓存中之前,允许图片被每个独立的 model 管理

How Fast Image Cache Works(Fast Image Cache 怎么工作的

In order to understand how Fast Image Cache works, it's helpful to understand a typical scenario encountered by many applications that work with images.

为了理解 Fast Image Cache 怎么工作的,有一个典型的,很多应用都会遇到的,关于处理大量图片相关的问题有助于你理解它工作的原理。

The Scenario(情形

iOS applications, especially those in the social networking space, often have many images to display at once, such as user photos. The intuitive, traditional approach is to request image data from an API, process the original images to create the desired sizes and styles, and store these processed images on the device.

iOS 中的应用,尤其是那些具备社交因素的应用,经常需要瞬间加载大量的图片,比如需要展示用户的图片。我们直觉上,能想到的方法就是去使用一个 API 来请求图片,处理这些原始的图片比如 size 和 style 什么的,然后将这些图片存储到设备上。

Later, when an application needs to display these images, they are loaded from disk into memory and displayed in an image view or are otherwise rendered to the screen.

之后,当应用需要显示这些图片的时候,你会将他们从磁盘读取到内存当中,然后在 UIImageView 中展示,也有可能直接渲染到屏幕上。

The Problem(问题

It turns out that the process of going from compressed, on-disk image data to a rendered Core Animation layer that the user can actually see is very expensive. As the number of images to be displayed increases, this cost easily adds up to a noticeable degradation in frame rate. And scrollable views further exacerbate the situation because content can change rapidly, requiring fast processing time to maintain a smooth 60FPS.1

很明显,处理磁盘上压缩的图片,直到在 Core Animation 层进行渲染后才能被用户看到,这个系统负担是非常高的。当要加载的图片不停增加时,系统负担将会直线上升,当用户在滑动这些 view 的时候,会进一步恶化,因为,图片内容会不停快速的改变,所以需要及时快速处理才能达到每秒 60 帧的效果。

Consider the workflow that occurs when loading an image from disk and displaying it on screen:

图片是如何被加载到屏幕上的,请看看加载过程中到底发生了什么:

  1. +[UIImage imageWithContentsOfFile:] uses Image I/O to create a CGImageRef from memory-mapped data. At this point, the image has not yet been decoded.
  2. +[UIImage imageWithContentsOfFile:] 使用 Image I/O 来创建了一个 CGImageRef ,并开辟了一个与图片大小匹配的内存。此时,图片还没有解码。 
  3. The returned image is assigned to a UIImageView.
  4. 这个图片被直接赋值给 UIImageView。
  5. An implicit CATransaction captures these layer tree modifications.
  6. 一个隐式的 CATransaction 捕捉到了这些 layer 树的修改情况。
  7. On the next iteration of the main run loop, Core Animation commits the implicit transaction, which may involve creating a copy of any images which have been set as layer contents. Depending on the image, copying it involves some or all of these steps: 2
    1. Buffers are allocated to manage file IO and decompression operations.
    2. The file data is read from disk into memory.
    3. The compressed image data is decoded into its uncompressed bitmap form, which is typically a very CPU-intensive operation.3
    4. The uncompressed bitmap data is then used by Core Animation to render the layer.

These costs can easily accumulate and kill perceived application performance. Especially while scrolling, users are presented with an unsatisfying user experience that is not in line with the the overall iOS experience.

这些开销会很容易的积累以及阻塞用户体验。尤其是在滑动的时候,用户会体验到卡顿。


60FPS ≈ 0.01666s per frame = 16.7ms per frame. This means that any main-thread work that takes longer than 16ms will cause your application to drop animation frames.

60FPS ≈ 0.01666s per frame = 16.7ms per frame 。这意味着,主线程,如果阻塞了超过 16 毫秒,会直接导致动画掉帧。

2 The documentation for CALayer's contents property states that "assigning a value to this property causes the layer to use your image rather than [creating] a separate backing store." However, the meaning of "use your image" is still vague. Profiling an application using Instruments often reveals calls to CA::Render::copy_image, even when the Core Animation Instrument has indicated that none of the images have been copied. One reason that Core Animation will require a copy of an image is improper byte alignment.

3 As of iOS 7, Apple does not make their hardware JPEG decoder available for third-party applications to use. As a result, only a slower, software decoder is used for this step.

3 在 iOS7 中,苹果不再提供他们的硬件 JPEG 解码器提供给第三方。所以只能使用速度更慢的软件解码。

The Solution(解决方法

Fast Image Cache minimizes (or avoids entirely) much of the work described above using a variety of techniques:

Fast Image Cache 使用了很多特别的技术来将上面描述的步骤最小化(或者是直接避免)。

Mapped Memory(内存映射

At the heart of how Fast Image Cache works are image tables. Image tables are similar to sprite sheets, often used in 2D gaming. An image table packs together images of the same dimensions into a single file. This file is opened once and is left open for reading and writing for as long as an application remains in memory.

Fast Image Cache 最为核心的原理就是图片分级存储(将类似的图片放在一个文件中)。图片分级存储就像一个精灵表单,2D 游戏中经常用到。它将具备相同尺寸的图片打包到一个文件当中。这个文件仅仅被打开一次,之后可以轻易的读写,直到应用程序退出为止。

Image tables use the mmap system call to directly map file data into memory. No memcpy occurs. This system call merely creates a mapping between data on disk and a region of memory.

图片分级存储使用了 mmap 系统,直接将文件读取到内存当中。不会导致 memcpy 产生。这个系统会产生一个介于磁盘和内存地址间的匹配关系。

When a request is made to the image cache to return a specific image, the image table finds (in constant time) the location of the desired image data in the file it maintains. That region of file data is mapped into memory, and a new CGImageRef whose backing store is the mapped file data is created.

当产生了一个需要图片的请求,图片分级存储会找到这个描述的图片 data ,这个 data 已经被映射到了内存当中,然后创建了一个 CGImageRef 。

When the returned CGImageRef (wrapped into a UIImage) is ready to be drawn to the screen, iOS's virtual memory system pages in the actual file data. This is another benefit of using mapped memory; the VM system will automatically handle the memory management for us. In addition, mapped memory "doesn't count" toward an application's real memory usage.

当这个 CGImageRef 准备在屏幕上进行绘制时,iOS 的内存分页系统已经得到了图片文件。这是另外一个使用了内存映射的好处。苹果的内存系统会自动的为我们管理这些。内存映射并没有实际增加了内存使用率。

In like manner, when image data is being stored in an image table, a memory-mapped bitmap context is created. Along with the original image, this context is passed to an image table's corresponding entity object. This object is responsible for drawing the image into the current context, optionally further configuring the context (e.g., clipping the context to a rounded rect) or doing any additional drawing (e.g., drawing an overlay image atop the original image). mmap marshals the drawn image data to disk, so no image buffer is allocated in memory.

在这个方法中,当图片被存储到了图片分级存储中,一个内存映射的上下文就被创建了。这个映射的上下文被传递到了整个对象当中,这个对象负责在当前的上下文中绘制图片,及时是创建保存一个新的绘制好的图片,mmap 会直接将这个新图片存储到磁盘上,所以,并没有开辟新的内存区域。

Uncompressed Image Data(解压后的图片数据

In order to avoid expensive image decompression operations, image tables store uncompressed image data in their files. If a source image is compressed, it must first be decompressed for the image table to work with it.This is a one-time cost. Furthermore, it is possible to utilize image format families to perform this decompression exactly once for a collection of similar image formats.

为了避免昂贵的图片解压操作,图片分级存储将解压后的图片存储到它自己管理的文件当中。如果一张图片解压了,那么,第一个就会从图片分级存储中去寻找,没找到就存储进去。解压操作仅仅执行一次。

There are obvious consequences to this approach, however. Uncompressed image data requires more disk space, and the difference between compressed and uncompressed file sizes can be significant, especially for image formats like JPEG. For this reason, Fast Image Cache works best with smaller images, although there is no API restriction that enforces this.

很明显这么做好处多多,然而,解压后的图片需要更大的磁盘空间,而且解压前与解压后的图片尺寸具有很大的不同,尤其是类似 JPEG 格式的图片。Fast Image Cache 最好用在小图片上,当然,没有 API 会限制你使用在大图片上哦。

Byte Alignment(字节对齐

For high-performance scrolling, it is critical that Core Animation is able to use an image without first having to create a copy. One of the reasons Core Animation would create a copy of an image is improper byte-alignment of the image's underlying CGImageRef. A properly aligned bytes-per-row value must be a multiple of 8 pixels × bytes per pixel. For a typical ARGB image, the aligned bytes-per-row value is a multiple of 64. Every image table is configured such that each image is always properly byte-aligned for Core Animation from the start. As a result, when images are retrieved from an image table, they are already in a form that Core Animation can work with directly without having to create a copy.

Considerations(需要考虑的地方

Image Table Size(图片表的尺寸

Image tables are configured by image formats, which specify (among other things) the maximum number of entries (i.e., individual images) an image table can have. This is to prevent the size of an image table file from growing arbitrarily.

图片表尺寸的配置基于图片的格式,图片格式确定了一个 table 能装载多少个实体对象。这是为了避免这个 table 任意的增长下去。

Image tables allocate 4 bytes per pixel, so the maximum space occupied by an image table file can be determined as follows:

一个像素会占用 4 byte 内存,所以,一个图片的 talbe 最大的尺寸可以由下面的方式来计算:

4 bytes per pixel × image width in pixels × image height in pixels × maximum number of entries

Applications using Fast Image Cache should carefully consider how many images each image table should contain. When a new image is stored in an image table that is already full, it will replace the least-recently-accessed image.

应用程序在使用 Fast Image Cache 时需要仔细考虑每一个图片 table 需要包含多少张图片。如果一个图片 table 已经满了,新添加进去的图片会替换掉最少使用的那张图片。

Image Table Transience(不要固化table

Image table files are stored in the user's caches directory in a subdirectory called ImageTables. iOS can remove cached files at any time to free up disk space, so applications using Fast Image Cache must be able to recreate any stored images and should not rely on image table files persisting forever.

图片被直接存储在用户的缓存目录中的一个子文件夹叫 ImageTables。iOS 会在任何时候来清空缓存释放资源。所以,使用了 Fast Image Cache 的应用程序必须能从重新的创建并存储图片,而不应该依赖于图片 talbe 永远的固化图片。

Note: As a reminder, data stored in a user's caches directory is not backed up to iTunes or iCloud.

注意:提醒你一下,存储在 caches 目录下的文件不会被 iCloud 以及 iTunes 备份的。

Source Image Persistence(源图片的固化

Fast Image Cache does not persist the original source images processed by entities to create the image data stored in its image tables.

For example, if an original image is resized by an entity to create a thumbnail to be stored in an image table, it is the application's responsibility to either persist the original image or be able to retrieve or recreate it again.

Image format families can be specified to efficiently make use of a single source image. See Working with Image Format Families for more information.

Data Protection

In iOS 4, Apple introduced data protection. When a user's device is locked or turned off, the disk is encrypted. Files written to disk are protected by default, although applications can manually specify the data protection mode for each file it manages. With the advent of new background modes in iOS 7, applications can now execute in the background briefly even while the device is locked. As a result, data protection can cause issues if applications attempt to access files that are encrypted.

Fast Image Cache allows each image format to specify the data protection mode used when creating its backing image table file. Be aware that enabling data protection for image table files means that Fast Image Cache might not be able to read or write image data from or to these files when the disk is encrypted.

Requirements(需要的环境

Fast Image Cache requires iOS 6.0 or greater and relies on the following frameworks:

Fast Image Cache 需要 iOS 6.0 或者更高,依赖于以下的框架:

  • Foundation
  • Core Graphics
  • UIKit

Note: As of version 1.1, Fast Image Cache does use ARC.

注意:1.1 版本时,Fast Image Cache 需要 ARC。


The FastImageCacheDemo Xcode project requires Xcode 5.0 or greater and is configured to deploy against iOS 6.0.

FastImageCacheDemo 的demo 需要 Xcode 5.0 或者更高。

Getting Started

Integrating Fast Image Cache

CocoaPods

For easy project integration, Fast Image Cache is available as a CocoaPod.

Manually

  • Clone this repository, or download the latest archive of master.
  • From the FastImageCache root directory, copy the source files from the inner FastImageCachesubdirectory to your Xcode project.
  • Import FICImageCache.h wherever you use the image cache.
  • Import FICEntity.h for each class that conforms to FICEntity.

Initial Configuration(初始化配置

Before the image cache can be used, it needs to be configured. This must occur each launch, so the application delegate might be a good place to do this.

在能使用这个图片缓存机制之前,需要它先提前配置。必须在程序启动前加载进去,所以,在 application delegate 中是个好地方。

Creating Image Formats(创建图片的格式

Each image format corresponds to an image table that the image cache will use. Image formats that can use the same source image to render the images they store in their image tables should belong to the same image format family. See Image Table Size for more information about how to determine an appropriate maximum count.

每一种图片格式响应一个图片列表。

static NSString *XXImageFormatNameUserThumbnailSmall = @"com.mycompany.myapp.XXImageFormatNameUserThumbnailSmall";
static NSString *XXImageFormatNameUserThumbnailMedium = @"com.mycompany.myapp.XXImageFormatNameUserThumbnailMedium";
static NSString *XXImageFormatFamilyUserThumbnails = @"com.mycompany.myapp.XXImageFormatFamilyUserThumbnails";FICImageFormat *smallUserThumbnailImageFormat = [[FICImageFormat alloc] init];
smallUserThumbnailImageFormat.name = XXImageFormatNameUserThumbnailSmall;
smallUserThumbnailImageFormat.family = XXImageFormatFamilyUserThumbnails;
smallUserThumbnailImageFormat.style = FICImageFormatStyle16BitBGR;
smallUserThumbnailImageFormat.imageSize = CGSizeMake(50, 50);
smallUserThumbnailImageFormat.maximumCount = 250;
smallUserThumbnailImageFormat.devices = FICImageFormatDevicePhone;
smallUserThumbnailImageFormat.protectionMode = FICImageFormatProtectionModeNone;FICImageFormat *mediumUserThumbnailImageFormat = [[FICImageFormat alloc] init];
mediumUserThumbnailImageFormat.name = XXImageFormatNameUserThumbnailMedium;
mediumUserThumbnailImageFormat.family = XXImageFormatFamilyUserThumbnails;
mediumUserThumbnailImageFormat.style = FICImageFormatStyle32BitBGRA;
mediumUserThumbnailImageFormat.imageSize = CGSizeMake(100, 100);
mediumUserThumbnailImageFormat.maximumCount = 250;
mediumUserThumbnailImageFormat.devices = FICImageFormatDevicePhone;
mediumUserThumbnailImageFormat.protectionMode = FICImageFormatProtectionModeNone;NSArray *imageFormats = @[smallUserThumbnailImageFormat, mediumUserThumbnailImageFormat];

An image format's style effectively determines the bit depth of the images stored in an image table. The following styles are currently available:

不同的图片格式决定了存储进图片 table 的位深。下面的这些格式是被支持的:

  • 32-bit color plus an alpha component (default)
  • 32-bit color, no alpha component
  • 16-bit color, no alpha component
  • 8-bit grayscale, no alpha component

If the source images lack transparency (e.g., JPEG images), then better Core Animation performance can be achieved by using 32-bit color with no alpha component. If the source images have little color detail, or if the image format's image size is relatively small, it may be sufficient to use 16-bit color with little or no perceptible loss of quality. This results in smaller image table files stored on disk.

如果图片资源没有透明像素,Core Animation 使用 32-bit 颜色的无 alpha 通道的组件会表现出好的效果。如果这个图片只有很少的颜色细节,或者这种格式的图片的尺寸很小,使用 16-bit 颜色会表现不错。

Configuring the Image Cache(配置图片的缓存

Once one or more image formats have been defined, they need to be assigned to the image cache. Aside from assigning the image cache's delegate, there is nothing further that can be configured on the image cache itself.

一旦一个或多种图片格式被定义了,此时就需要把它们赋给图片缓存了。除了设置一个代理,没有更多的东西可以设置了。

FICImageCache *sharedImageCache = [FICImageCache sharedImageCache];
sharedImageCache.delegate = self;
sharedImageCache.formats = imageFormats;

Creating Entities(创建实体对象

Entities are objects that conform to the FICEntity protocol. Entities uniquely identify entries in an image table, and they are also responsible for drawing the images they wish to store in the image cache. Applications that already have model objects defined (perhaps managed by Core Data) are usually appropriate entity candidates.

实体对象需要符合 FICentity 协议。实体对象的唯一标示了一个图片table。它们也负责存储绘制过的图片到图片缓存中。

@interface XXUser : NSObject <FICEntity>@property (nonatomic, assign, getter = isActive) BOOL active;
@property (nonatomic, copy) NSString *userID;
@property (nonatomic, copy) NSURL *userPhotoURL;@end

Here is an example implementation of the FICEntity protocol.

- (NSString *)UUID {CFUUIDBytes UUIDBytes = FICUUIDBytesFromMD5HashOfString(_userID);NSString *UUID = FICStringWithUUIDBytes(UUIDBytes);return UUID;
}- (NSString *)sourceImageUUID {CFUUIDBytes sourceImageUUIDBytes = FICUUIDBytesFromMD5HashOfString([_userPhotoURL absoluteString]);NSString *sourceImageUUID = FICStringWithUUIDBytes(sourceImageUUIDBytes);return sourceImageUUID;
}- (NSURL *)sourceImageURLWithFormatName:(NSString *)formatName {return _sourceImageURL;
}- (FICEntityImageDrawingBlock)drawingBlockForImage:(UIImage *)image withFormatName:(NSString *)formatName {FICEntityImageDrawingBlock drawingBlock = ^(CGContextRef context, CGSize contextSize) {CGRect contextBounds = CGRectZero;contextBounds.size = contextSize;CGContextClearRect(context, contextBounds);// Clip medium thumbnails so they have rounded cornersif ([formatName isEqualToString:XXImageFormatNameUserThumbnailMedium]) {UIBezierPath clippingPath = [self _clippingPath];[clippingPath addClip];}UIGraphicsPushContext(context);[image drawInRect:contextBounds];UIGraphicsPopContext();};return drawingBlock;
}

Ideally, an entity's UUID should never change. This is why it corresponds nicely with a model object's server-generated ID in the case where an application is working with resources retrieved from an API.

An entity's sourceImageUUID can change. For example, if a user updates their profile photo, the URL to that photo should change as well. The UUID remains the same and identifies the same user, but the changed profile photo URL will indicate that there is a new source image.

Note: Often, it is best to hash whatever identifiers are being used to define UUID and sourceImageUUID. Fast Image Cache provides utility functions to do this. Because hashing can be expensive, it is recommended that the hash be computed only once (or only when the identifier changes) and stored in an instance variable.

When the image cache is asked to provide an image for a particular entity and format name, the entity is responsible for providing a URL. The URL need not even point to an actual resource—e.g., the URL might be constructed of a custom URL-scheme—, but it must be a valid URL.

The image cache uses these URLs merely to keep track of which image requests are already in flight; multiple requests to the image cache for the same image are handled correctly without any wasted effort. The choice to use URLs as a basis for keying image cache requests actually complements many real-world application designs whereby URLs to image resources (rather than the images themselves) are included with server-provided model data.

Note: Fast Image Cache does not provide any mechanism for making network requests. This is the responsibility of the image cache's delegate.

Finally, once the source image is available, the entity is asked to provide a drawing block. The image table that will store the final image sets up a file-mapped bitmap context and invokes the entity's drawing block. This makes it convenient for each entity to decide how to process the source image for particular image formats.

Requesting Images from the Image Cache(从图片缓存中请求图片

Fast Image Cache works under the on-demand, lazy-loading design pattern common to Cocoa.

Fast Image Cache 使用了懒加载设计模式:

XXUser *user = [self _currentUser];
NSString *formatName = XXImageFormatNameUserThumbnailSmall;
FICImageCacheCompletionBlock completionBlock = ^(id <FICEntity> entity, NSString *formatName, UIImage *image) {_imageView.image = image;[_imageView.layer addAnimation:[CATransition animation] forKey:kCATransition];
};BOOL imageExists = [sharedImageCache retrieveImageForEntity:user withFormatName:formatName completionBlock:completionBlock];if (imageExists == NO) {_imageView.image = [self _userPlaceholderImage];
}

There are a few things to note here.

  1. Note that it is an entity and an image format name that uniquely identifies the desired image in the image cache. As a format name uniquely identifies an image table, the entity alone uniquely identifies the desired image data in an image table.
  2. The image cache never returns a UIImage directly. The requested image is included in the completion block. The return value will indicate whether or not the image already exists in the image cache.
  3. -retrieveImageForEntity:withFormatName:completionBlock: is a synchronous method. If the requested image already exists in the image cache, the completion block will be called immediately. There is an asynchronous counterpart to this method called -asynchronouslyRetrieveImageForEntity:withFormatName:completionBlock:.
  4. If a requested image does not already exist in the image cache, then the image cache invokes the necessary actions to request the source image for its delegate. Afterwards, perhaps some time later, the completion block will be called.

Note: The distinction of synchronous and asynchronous only applies to the process of retrieving an image that already exists in the image cache. In the case where a synchronous image request is made for an image that does not already exist in the image case, the image cache does not block the calling thread until it has an image. The retrieval method will immediately return NO, and the completion block will be called later.

See the FICImageCache class header for a thorough explanation of how the execution lifecycle works for image retrieval, especially as it relates to the handling of the completion blocks.

Providing Source Images to the Image Cache(给图片缓存提供图片)

There are two ways to provide source images to the image cache.

  1. On Demand: This is the preferred method. The image cache's delegate is responsible for supplying the image cache with source images.

    - (void)imageCache:(FICImageCache *)imageCache wantsSourceImageForEntity:(id<FICEntity>)entity withFormatName:(NSString *)formatName completionBlock:(FICImageRequestCompletionBlock)completionBlock {dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// Fetch the desired source image by making a network requestNSURL *requestURL = [entity sourceImageURLWithFormatName:formatName];UIImage *sourceImage = [self _sourceImageForURL:requestURL];dispatch_async(dispatch_get_main_queue(), ^{completionBlock(sourceImage);});});
    }    
    

    This is where the URL-based nature of how the image cache manages image requests is convenient. First, an image retrieval request to the image cache for an image that is already being handled by the image cache's delegate—e.g., waiting on a large image to be downloaded—is simply added to the first request's array of completion blocks. Second, if source images are downloaded from the Internet (as is often the case), the URL for such a network request is readily available.

    Note: The completion block must be called on the main thread. Fast Image Cache is architected such that this call will not block the main thread, as processing sources image is handled in the image cache's own serial dispatch queue.

  2. Manually: It is possible to manually insert image data into the image cache.

    // Just finished downloading new user photoXXUser *user = [self _currentUser];
    NSString *formatName = XXImageFormatNameUserThumbnailSmall;
    FICImageCacheCompletionBlock completionBlock = ^(id <FICEntity> entity, NSString *formatName, UIImage *image) {NSLog(@"Processed and stored image for entity: %@", entity);
    };[sharedImageCache setImage:newUserPhoto forEntity:user withFormatName:formatName completionBlock:completionBlock];
    

Note: Fast Image Cache does not persist source images. See Source Image Persistence for more information.

Canceling Source Image Requests(取消图片请求

If an image request is already in progress, it can be cancelled:

// We scrolled up far enough that the image we requested in no longer visible; cancel the request
XXUser *user = [self _currentUser];
NSString *formatName = XXImageFormatNameUserThumbnailSmall;
[sharedImageCache cancelImageRetrievalForEntity:user withFormatName:formatName];

When this happens, Fast Image Cache cleans up its internal bookkeeping, and any completion blocks from the corresponding image request will do nothing at this point. However, the image cache's delegate is still responsible for ensuring that any outstanding source image requests (e.g., network requests) are cancelled:

- (void)imageCache:(FICImageCache *)imageCache cancelImageLoadingForEntity:(id <FICEntity>)entity withFormatName:(NSString *)formatName {[self _cancelNetworkRequestForSourceImageForEntity:entity withFormatName:formatName];
}

Working with Image Format Families

The advantage of classifying image formats into families is that the image cache's delegate can tell the image cache to process entity source images for all image formats in a family when any image format in that family is processed.

- (BOOL)imageCache:(FICImageCache *)imageCache shouldProcessAllFormatsInFamily:(NSString *)formatFamily forEntity:(id<FICEntity>)entity {BOOL shouldProcessAllFormats = NO;if ([formatFamily isEqualToString:XXImageFormatFamilyUserThumbnails]) {XXUser *user = (XXUser *)entity;shouldProcessAllFormats = user.active;}return shouldProcessAllFormats;
}

The advantage of processing all image formats in a family at once is that the source image does not need to be repeatedly downloaded (or loaded into memory if cached on disk).

For example, if a user changes their profile photo, it probably makes sense to process the new source image for every variant at the same time that the first image format is processed. That is, if the image cache is processing a new user profile photo for the image format named XXImageFormatNameUserThumbnailSmall, then it makes sense to also process and store new image data for that same user for the image format named XXImageFormatNameUserThumbnailMedium.

Documentation

Fast Image Cache's header files are fully documented, and appledoc can be used to generate documentation in various forms, including HTML and Xcode DocSet.

HTML documentation can be found here.

Demo Application

Included with this repository is a demo app Xcode project. It demonstrates the difference between the conventional approach for loading and displaying images and the Fast Image Cache approach. See therequirements for running the demo app Xcode project.

Note: The demo application must either be supplied with JPEG images, or the included fetch_demo_images.sh script in the FastImageCacheDemo directory must be run.

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/YouXianMing/p/3661773.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/258739.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php练习 租房子

题目要求 1.封装类 <?php class DBDA {public $fuwuqi"localhost"; //服务器地址public $yonghuming"root";//用户名public $mima"";//密码 public $dbconnect;//连接对象//操作数据库的方法//$sql代表需要执行的SQL语句//$type代表SQL语…

centos 安装boost(caffe需要)

安装 由于安装caffe&#xff0c;要求boost的版本在1.55以上&#xff0c;而服务器上的刚好是1.54,所以进行了重装。 参考&#xff1a;《CentOS 7下编译安装Boost_1_57_0 》 不过由于pycaffe需要boost.python,因此需要在./b2时修改为./b2 –stage debug 才可以。而不能去掉py…

JAVA正则表达式介绍和使用

本文引用自 http://www.cnblogs.com/android-html5/archive/2012/06/02/2533924.html 技术博客 1.Java中在某个字符串中查询某个字符或者某个子字串 Java代码 String s "Shang Hai Hong Qiao Fei Ji Chang";    String regEx "a|F"; //表示a或F Pat…

集合框架中的接口及其实现类

Collection&#xff1a;集合层次中的根接口&#xff0c;JDK没有提供这个接口直接地实现类。Set&#xff1a;不能包含重复的元素。SortedSet是一个按照升序排列元素的Set。List&#xff1a;是一个有序的集合&#xff0c;可以包含重复的元素。提供了按索引访问的方式。Map&#x…

C# 多线程 Parallel.For 和 For 谁的效率高?那么 Parallel.ForEach 和 ForEach 呢?

还是那句话&#xff1a;十年河东&#xff0c;十年河西&#xff0c;莫欺少年穷。 今天和大家探讨一个问题&#xff1a;Parallel.For 和 For 谁的效率高呢&#xff1f; 从CPU使用方面而言&#xff0c;Parallel.For 属于多线程范畴&#xff0c;可以开辟多个线程使用CPU内核&#x…

bigdecimal 小于等于0_图解小于 K 的两数之和

点击蓝色“五分钟学算法”关注我哟加个“星标”&#xff0c;天天中午 12:15&#xff0c;一起学算法作者 | P.yh来源 | 五分钟学算法题目描述 题目来源于 LeetCode 上第 1099 号问题&#xff1a;小于 K 的两数之和。给你一个整数数组 A 和一个整数 K&#xff0c;请在该数组中找出…

pdf 深入理解kotlin协程_Kotlin协程实现原理:挂起与恢复

今天我们来聊聊Kotlin的协程Coroutine。如果你还没有接触过协程&#xff0c;推荐你先阅读这篇入门级文章What? 你还不知道Kotlin Coroutine?如果你已经接触过协程&#xff0c;但对协程的原理存在疑惑&#xff0c;那么在阅读本篇文章之前推荐你先阅读下面的文章&#xff0c;这…

编译py-faster-rcnn的问题汇总及解决方法

按照官网 的提示&#xff0c;我开始安装faster rcnn&#xff0c;但是出现了很多问题&#xff0c;我将其汇总了起来&#xff0c;并提出了解决办法。 先说明一下我的配置&#xff1a; python : anaconda2linux: centos 6.9 安装faster rcnn请先参考&#xff1a;《cuda8cudnn4 F…

linux 安装python-opencv

三种方法&#xff1a; 1. pip 安装 &#xff1a; pip install opencv-python &#xff0c;最新版为opencv3安装后>>> import cv2 >>> print cv2.__version__参考&#xff1a;http://www.cnblogs.com/lclblack/p/6377710.html 2. anaconda的conda安装 ,可以指…

《你的灯亮着吗》读书笔记Ⅲ

转载于:https://www.cnblogs.com/yue3475975/p/4586220.html

nvidia显卡对比分析

本文章转载自&#xff1a;http://www.cnblogs.com/lijingcong/p/4958617.html 科学计算显卡的两个主要性能指标&#xff1a;1、CUDA compute capability&#xff0c;这是英伟达公司对显卡计算能力的一个衡量指标&#xff1b;2、FLOPS 每秒浮点运算次数&#xff0c;TFLOPS表示每…

零基础不建议学前端_web前端开发零基础怎样入门-哈尔滨前端学习

web前端开发零基础怎样入门-哈尔滨前端学习&#xff0c;俗话说&#xff0c;知己知彼&#xff0c;百战百胜。要想学好web前端&#xff0c;首先要了解什么是web前端&#xff0c;下面由小编来给大家介绍一下&#xff1a;1什么是web&#xff1f;Web就是在Http协议基础之上, 利用浏览…

SpringBoot的配置项

2019独角兽企业重金招聘Python工程师标准>>> spring Boot 其默认是集成web容器的&#xff0c;启动方式由像普通Java程序一样&#xff0c;main函数入口启动。其内置Tomcat容器或Jetty容器&#xff0c;具体由配置来决定&#xff08;默认Tomcat&#xff09;。当然你也可…

北大OJ百练——4075:矩阵旋转(C语言)

百练的这道题很简单&#xff0c;通过率也达到了86%&#xff0c;所以我也就来贴个代码了。。。下面是题目&#xff1a; 不过还是说一下我的思路&#xff1a; 这道题对一个新来说&#xff0c;可能是会和矩阵的转置相混淆&#xff0c;这题并不是要我们去求矩阵的转置。 这题&#…

编译py-faster-rcnn全过程

编译py-faster-rcnn&#xff0c;花费了好几天&#xff0c;中间遇到好多问题&#xff0c;今天终于成功编译。下面详述我的整个编译过程。 【注记&#xff1a;】其实下面的依赖库可以安装在统一的一个本地目录下&#xff0c;相关安装指南&#xff0c;可以参考《深度学习&#xf…

不是世界不好,而是你见得太少

转载于:https://www.cnblogs.com/yymn/p/4590333.html

用Heartbeat实现web服务器高可用

用Heartbeat实现web服务器高可用heartbeat概述: Heartbeat 项目是 Linux-HA 工程的一个组成部分&#xff0c;它实现了一个高可用集群系统。心跳服务和集群通信是高可用集群的两个关键组件&#xff0c;在 Heartbeat 项目里&#xff0c;由 heartbeat 模块实现了这两个功能。端口号…

scp创建远程目录_在Linux系统中使用Vim读写远程文件

大家好&#xff0c;我是良许。 今天我们讨论一个 Vim 使用技巧——用 Vim 读写远程文件。要实现这个目的&#xff0c;我们需要使用到一个叫 netrw.vim 的插件。从 Vim 7.x 开始&#xff0c;netrw.vim 就被设置为默认安装的标准插件了。这个插件允许用户通过 ftp、rcp、scp、htt…

softmax logistic loss详解

softmax函数–softmax layer softmax用于多分类过程中&#xff0c;它将多个神经元的输出&#xff0c;映射到&#xff08;0,1&#xff09;区间内&#xff0c;可以看成概率来理解&#xff0c;从而来进行多分类&#xff01; 假设我们有一个数组z(z1,z2,...zm),则其softmax函数定…

javascript做极简时钟特效,再简单没思路你也做不出来

点击查看时钟特效极简主义&#xff0c;程序员javascript打造极简时钟特效对于javascript特效的学习&#xff0c;重要的是逻辑思路&#xff0c;所以这个时钟特效不是很华丽&#xff0c;但是功能都展现出来了&#xff0c;而学习javascript并不是单纯的扣代码&#xff0c;很多人都…