High resolution image loading in iPhone4/4S is easy in native SDK, so that is why we wanted to support that simplicity in MobiAccess. However, we ran into some problems implementing that simple solution.
The @2x image loading in the iOS SDK is supported only if we load the image with the [UIImage imageNamed:fileName] method.
But in MobiAccess, creating images can only be done from NSInputStream, for example:
Image.FromStream(Resources.GetResourceAsStream(fileName)).
This actually creates an NSInputStream and passes it as a parameter to the Image Constructor, which reads it and creates the image with [UIImage imageWithData:data]. The problem with that at this point is that we do not have a clue as to where that NSInputStream points to. Without knowing the filename, we cannot use the native support for @2x image loading.
The MobiAccess virtual machine has a complex and strict class hierarchy, so it is not easy to make modifications in the VM itself without implementing the modification on all platforms.
In this case, we needed to attach an NSString (the filename) to the NSInputStream, without adding a new field or subclassing it.
We wanted to solve this in a very clean way and objective C's associative references popped into my head. I read about it in a book called iOS5 Programming: Pushing the Limits by Rob Napier and Mugunth Kumar. Altogether it is a very informative and intriguing book.
According to the book, associative references are used to attach key-value data to any NSObject.
In order to use associative references, we need to import objc runtime:
#import <objc/runtime.h>
This is how we associate the input stream with the filename using key pathKey:
/*
Attaching the fileName to the stream with association. This is required because we need the filename in Image.FromStream in order to load the @2x images for iphone4 properly
more info: http://developer.apple.com/library/IOs/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocAssociativeReferences.html
*/
objc_setAssociatedObject(is, &pathKey, fileName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
And this is how we get back the associated filename:
/*
We associated the filename with the inputstream in Resources.GetResourceAsStream(), in order to be able to load the @2x images for iphone4 here.
[UIImage imageNamed:fileName] automatically loads the highres version of the image if available.
*/
NSString* fileName = objc_getAssociatedObject(is, &pathKey);
You can see OBJC_ASSOCIATION_RETAIN_NONATOMIC enum is used as third parameter in the setter. Associations have proper memory management, and they are freed when the object is being freed so you do not have to worry about it.
Written by PS