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:
圖片是如何被加載到屏幕上的,請看看加載過程中到底發生了什么:
+[UIImage imageWithContentsOfFile:]
?uses?Image I/O?to create a?CGImageRef
?from memory-mapped data. At this point, the image has not yet been decoded.+[UIImage imageWithContentsOfFile:]
?使用 Image I/O 來創建了一個 CGImageRef ,并開辟了一個與圖片大小匹配的內存。此時,圖片還沒有解碼。?- The returned image is assigned to a?
UIImageView
. - 這個圖片被直接賦值給 UIImageView。
- An implicit?
CATransaction
?captures these layer tree modifications. - 一個隱式的 CATransaction 捕捉到了這些 layer 樹的修改情況。
- 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
- Buffers are allocated to manage file IO and decompression operations.
- The file data is read from disk into memory.
- The compressed image data is decoded into its uncompressed bitmap form, which is typically a very CPU-intensive operation.3
- 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.
這些開銷會很容易的積累以及阻塞用戶體驗。尤其是在滑動的時候,用戶會體驗到卡頓。
1?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.
1?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?FastImageCache
subdirectory 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.
- 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.
- 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. -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:
.- 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.
-
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.
-
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.
?
?
?
?
?
?
?
?
?