CZSharedImage 0.4.0

CZSharedImage 0.4.0

TestsTested
LangLanguage Obj-CObjective C
License MIT
ReleasedLast Release Dec 2014

Maintained by Mark Smith.



  • By
  • Mark Smith

On iOS, the UIImage method imageNamed: optimizes image loading in two ways:

  1. It caches recently loaded UIImage objects to avoid reloading them.
  2. Multiple requests for the same named image get a reference to the same UIImage object.

The first optimization saves (loading) time at the cost of (memory) space.

For images that currently exist as UIImage objects in the running application, the second optimization saves both time and space; there's no need to load or decode the image, and there aren't duplicate copies each occupying memory.

CZSharedImage is a tiny library that provides the second optimization for images loaded using UIImage#imageWithContentsOfFile: and UIImage#imageWithData:.

Note that CZSharedImage is not a cache; it simply tracks live UIImage objects. Its value is in cases where an image is used multiple times in a view (accessory images in a table view, for instance), or multiple times in a navigation hierarchy.

Usage

CZSharedImage requires ARC and iOS 6.0. (It uses the NSMapTable class introduced in 6.0.)

Import the header file CZSharedImage.h.

For usage examples, see CZSharedImageTests.m or the example app.

In the example app, 100 instances of a 500x500 image (each occupying approximately 1MB of memory when decoded) are displayed on the screen. The resident size of the app when using UIImage#imageWithContentsOfFile: is 99MB more than when using CZSharedImage#imageWithContentsOfFile:.

CZSharedImage class

Shared images are created using the CZSharedImage class.

Creating images

+ (UIImage *)imageWithContentsOfFile:(NSString *)path;
+ (UIImage *)imageWithData:(NSData *)data;
+ (UIImage *)imageWithData:(NSData *)data scale:(CGFloat)scale;

Simply replace calls to the above three UIImage methods with the CZSharedImage methods of the same name. If the associated UIImage object already exists in the running app, the method will immediately return a reference to that object. If not, it will be loaded as usual by the corresponding UIImage initializer.

UIImage *image = [CZSharedImage imageWithContentsOfFile:@"/path/to/image/file"];

Note that in the case of images loaded using imageWithData:, subsequent requests are matched using an MD5 hash of the data. A more efficient mechanism is to manually associate the image with a chosen path; see the next section for details.

Associating images with paths

+ (UIImage *)imageForPath:(NSString *)path;
+ (void)setImage:(UIImage *)image forPath:(NSString *)path;

For cases where an image is loaded using some mechanism other than the three imageWith... methods (for instance, over the network), it can still be manually associated with a path such as the URL for subsequent requests:

// Try to get a reference to this image.

NSString *path = @"http://example.com/foo.png";
UIImage *image = [CZSharedImage imageForPath:path];

// If not available, fetch the image and associate it with the URL.
// (Error handling not shown.)

if (!image) {
  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:path]];
  [NSURLConnection sendAsynchronousRequest:request
                                     queue:[NSOperationQueue mainQueue]
                         completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
                         {
                             // While the fetched UIImage object below remains alive,
                             // CZSharedImage#imageForPath: will return a reference to it.

                             UIImage *image = [UIImage imageWithData:data];
                             [CZSharedImage setImage:image forPath:path];
                             ...
                         }];
}
...

Similarly, an image fetched from a database could be associated with a unique path corresponding to the database row (and column if necessary):

// Try to get a reference to this image.

int personid = <...get row ID...>;
NSString *path = [NSString stringWithFormat:@"db:/person/%d", personid];
UIImage *image = [CZSharedImage imageForPath:path];

// If not available, fetch the image and associate it with the path.
// (Error handling not shown.)

if (!image) {
  NSData *data = <...load image data...>;

  // While the fetched UIImage object below remains alive,
  // CZSharedImage#imageForPath: will return a reference to it.

  UIImage *image = [CZSharedImage imageWithData:data];
  [CZSharedImage setImage:image forPath:path];
}
...