c31983
c31983

Reputation: 449

Using UIImageView as key in NSMutableDictionary to look up boolean

The title pretty much says it all. I want to create an NSMutableDictionary where I use UIImageViews to look up boolean values. Here's what it looks like:

My .h file:

@interface ViewController : UIViewController{
    UIImageView *image1;
    UIImageView *image2;
    NSMutableDictionary *isUseable;
}

@property (nonatomic, retain) IBOutlet UIImageView *image1;
@property (nonatomic, retain) IBOutlet UIImageView *image2;
@property (nonatomic, retain) NSMutableDictionary *isUseable;

My .m file:

@synthesize image1, image2;
@synthesize isUseable

- (void)viewDidLoad
{
    isUseable = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                 @NO, image1,
                 @NO, image2,nil];
}

-(void)runEvents{
    if(some condition){
        [isUseable setObject:@YES forKey:(id)image1];
    }

    //Use it later:
    if(isUseable[image1]){
        //Do stuff
    }
}

It compiles but when I run it I get an uncaught exception 'NSInvalidArgumentException', reason: '-[UIImageView copyWithZone:]: unrecognized selector sent to instance.

I'm guessing that the problem lies with the fact that the NSDictionary class copies its keys. Is there a way to get a dictionary working in this case? If not, how should I set up a lookup like the one I want? Any ideas / suggestions?

Upvotes: 1

Views: 946

Answers (5)

Albert Renshaw
Albert Renshaw

Reputation: 17902

Depending on how your object is managed you may be able to use its address in memory as a key (This applies to both UIImageView or UIImage since the general discussion on this question seems to revolve around either/or):

myDictionary[@((intptr_t)myObject)] = myValue;

Upvotes: 0

Albert Renshaw
Albert Renshaw

Reputation: 17902

Get the raw data from the image (not a re-rendered PNG or JPG representation) and then run it through built in common-crypto to get SHA256 of the data, this will be your key.

Other answers suggest using the built in instance property .hash but it is prone to collision, and they end up happening frequently (We experienced a string collision in one of our apps and it caused a severe bug).

#include <CommonCrypto/CommonDigest.h>

-(NSString *)keyFromImage:(UIImage *)image {

    NSData *imageData = (__bridge NSData *)CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
    
    unsigned char result[CC_SHA256_DIGEST_LENGTH];
    CC_SHA256((__bridge const void *)imageData, (CC_LONG)imageData.length, result);
    
    NSMutableString *imageHash = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH*2];
    
    for(int i = 0; i<CC_SHA256_DIGEST_LENGTH; i++) {
        [imageHash appendFormat:@"%02x",result[i]];
    }
    
    return imageHash;

}

Upvotes: 0

Ben Lu
Ben Lu

Reputation: 3042

rmaddy said it pretty right: an NSDictionary must conform to the NSCopying protocol and UIImage does not.

I recommend to use @(image.hash) as dictionary key. It's like a fingerprint of UIImage.

Upvotes: 3

Sebastian
Sebastian

Reputation: 7720

Keys in NSDictionaries have to conform to NSCopying and UIImageView doesn't. You will have to find a different key, or you could extend UIImageView to conform to NSCopying. See this answer on SO for how to do it with a UIImage.

Upvotes: 1

rmaddy
rmaddy

Reputation: 318904

Yes, the problem is that keys in an NSDictionary must conform to the NSCopying protocol and UIImage does not.

One solution would be to give each image a unique tag. Then use the image's tag as the key (after wrapping it in an NSNumber).

- (void)viewDidLoad {
    isUseable = [ @{ @(image1.tag) : @NO, @(image2.tag) : @NO } mutableCopy];
}

-(void)runEvents {
    if(some condition) {
        [isUseable setObject:@YES forKey:@(image1.tag)];
    }

    //Use it later:
    if(isUseable[@(image1.tag)]) {
        //Do stuff
    }
}

Just add the code to see each image's tag property.

Upvotes: 4

Related Questions