3

I can set UIColor objects as keys in an NSMutableDictionary all day long and everything is fine and happy...

For instance:

[myDict setObject:foo forKey:[UIColor redColor]]; 

That works just fine... UNLESS I try to use the following:

UIColor *black = [[UIColor alloc] initWithRed:0 green:0 blue:0 alpha:1];
[myDict setObject:foo forKey:black];

That always gives me:

-[UIDeviceRGBColor copyWithZone:]: unrecognized selector sent to instance 0x691be80

The reasons for why I'm defining black in the RGB colorspace are unimportant to this question, just know that I must define it that way. What I don't understand is why is this and only this color causing me a problem, and why is it that the error is a copyWithZone error?

For the record, [UIColor blackColor] works as a key, but because it isn't an RGB colorspace it is not suitable for my application.

The McG
  • 175
  • 2
  • 14

1 Answers1

4

It looks like the better question is "why does every other UIColor instance work as a key"?

Keys in an NSMutableDictionary "must conform to the NSCopying protocol". UIColor objects don't conform to that protocol per the documentation, so you're theoretically unable to use them as keys in a dictionary.

In practice, I might guess that you're getting various concrete subclasses from a class cluster that's hidden by UIColor, and maybe some of them do, in fact, support NSCopying, but UIDeviceRGBColor does not seem to.

You could wrap the UIColor object in a thin wrapper object that did support NSCopying, and override -isEqualTo: etc to get the comparisons to work correctly, and then use those wrappers in the dictionary.

Ben Zotto
  • 70,108
  • 23
  • 141
  • 204
  • Yes this seems to make senese. I wonder if you could implement copyWithZone in your own UIDeviceRGBColor category. – D.C. Dec 16 '10 at 03:50
  • Interesting! I didn't think to check since all the predefined UIColors are working as keys... If I did a Category I'd need to override hash as well, correct? – The McG Dec 16 '10 at 03:59
  • 2
    Adding the following code in a category on UIColor seems to have fied the problem: -(id)copyWithZone:(NSZone *)zone { CGColorRef cgcopy = CGColorCreateCopy([self CGColor]); UIColor *copy = [[[self class] allocWithZone:zone] initWithCGColor:cgcopy]; return copy; } – The McG Dec 16 '10 at 15:39
  • @MGM-Diatom: Cool idea. This seems like it'll work too. Be careful, as you're leaking the CGColorRef you create here (once you've inited the UIColor with it, you can release it). – Ben Zotto Dec 16 '10 at 17:17