Graphics Reference
In-Depth Information
Deferred Decompression
Once an image file has been loaded, it must then be decompressed. This decompression can
be a computationally complex task and take considerable time. The decompressed image
will also use substantially more memory than the original.
The relative CPU time spent loading versus decompressing will depend on the image
format. For PNG images, loading takes longer than for JPEGs because the file is
proportionally larger, but decompression is relatively fast, especially since Xcode
recompresses any PNGs included in the project using optimal settings for fast decoding.
JPEG images are smaller and load quicker, but the decompression step is more expensive
because the JPEG decompression algorithm is more complex than the zip-based algorithm
used in PNG.
When you load an image, iOS usually defers decompression until later to conserve
memory. This can cause a performance hiccup when you actually try to draw the image, as
it has to be decompressed
at the point of drawing
(which is often the worst possible time).
The simplest way to avoid deferred decompression is to load images using the
UIImage
+imageNamed:
method. Unlike
+imageWithContentsOfFile:
(and all the other
UIImage
loading methods), this method decompresses the image immediately after loading (as well
as having other benefits that we discuss later in the chapter). The problem is that
+imageNamed:
works only for images loaded from within the application resources bundle,
so it's not an option for user-generated content, or downloaded images.
Another way to decompress an image immediately is to assign it as the
contents
of a layer
or as the
image
property of a
UIImageView
. Unfortunately, this has to be done on the main
thread and so usually won't help with performance problems.
A third approach is to bypass UIKit altogether and load the image using the ImageIO
framework instead, as follows:
NSInteger
index = indexPath.row;
NSURL
*imageURL = [
NSURL
fileURLWithPath
:
self
.
imagePaths
[index]];
NSDictionary
*options =
@{
(
__bridge
id
)
kCGImageSourceShouldCache
:
@YES}
;
CGImageSourceRef
source =
CGImageSourceCreateWithURL
(
(
__bridge
CFURLRef
)imageURL,
NULL
);
CGImageRef
imageRef =
CGImageSourceCreateImageAtIndex
(source,
0
,
(
__bridge
CFDictionaryRef
)options);
UIImage
*image = [UIImage imageWithCGImage:imageRef];
CGImageRelease
(imageRef);
CFRelease
(source);
This allows you to make use of the
kCGImageSourceShouldCache
option when creating the
image, which forces it to decompress immediately and retain the decompressed version for
the lifetime of the image.