views:

659

answers:

5

I'm writing a contextual "factory" that will maintain a dictionary of converter/acting objects which inherit from some Converter class. This class has a method:

- (Class)classResponsibility

Or something similar, such that a StringConverter class would implement the method as:

- (Class)classResponsibility {
    return [NSString class];
}

Then to store that converter in the dictionary, I had hoped on doing something like:

[converters setValue:stringConverter forKey:[stringConverter classResponsibility]];

But the compiler complains that the type "Class" is an invalid parameter type for argument 2 of the setValue:forKey: method. I had wanted to avoid setting the key as the Class's name ("NSString"), but if that's the best solution than I'll go with it.

+2  A: 

-setValue:forKey: is documented to take an NSString as the second parameter. You'll have to use NSStringFromClass() and NSClassFromString() as adaptors.

Graham Lee
You are right about Class being an object
Jarret Hardie
Don't the docs for setValue:forKey: say that the key only has to be an object when using key-value coding? Otherwise, couldn't it be any object that implements NSCopying protocol, like NSDictionary?
Jarret Hardie
+1  A: 

I was looking for the setObject:forKey: method instead of setValue:forKey:. The method signature for setObject:forKey: accepts (id) as both parameter types, and is much better suited.

craig
Classes don't implement NSCopying, so can't be used as the key in -setObject:forKey:. Really that parameter should be (id <NSCopying>), I'm not sure why it's not.
Graham Lee
+3  A: 

A class object (type Class) isn't actually an Objective-C object; it's a pointer to an opaque objc_class struct (you can see this in 'objc.h'). Because the keys to an NSDictionary have to be objects conforming to the NSCopying protocol, a Class isn't going to work for a key.

The simplest thing to do is call NSStringFromClass and use the name of the class as the key, as Graham Lee suggested.

The difference between setObject:forKey: and setValue:forKey: is that the latter is part of the key-value coding infrastructure. In general you'll probably want to prefer setObject:forKey: when dealing with NSMutableDictionary as it carries less semantic baggage and doesn't require your keys to be NSStrings (that's my opinion; in practice I suppose it's really more a matter of style).

Skirwan
+1  A: 

I just had a similar situation crop up with the exact same error message:

[tempDictionary setObject:someDictionary forKey:someClass];

All I did was implement the NSCopying protocol in someClass:

- (id)copyWithZone:(NSZone *)zone
{
    id copy = [[[self class] allocWithZone:zone] init];
    [copy setId:[self id]];
    [copy setTitle:[self title]];
    return copy;
}

I think what was happening was that a copy of someClass was being made in order to be used as the key, but since my object didn't know how to copy itself (deriving from NSObject it didn't have a copyWithZone in the superclass) it balked.

One thing I've found with my approach is that it's use an object as a key. Unless I already have the object instantiated, I'm constantly calling allKeys or just otherwise enumerating over the dictionary.

[After writing this, I see that you want to store the class as such as the key. I'm leaving this out there because I would have saved a lot of time if I had found my answer when I was searching SO. I didn't find anything like this then.]

bbrown
+2  A: 

Your other option is to use [NSValue valueWithNonretainedObject:yourObjectHere] to construct the key from something other than a string. I ran into a similar problem and I wanted to use a CoreData object as the key and something else as the value. This NSValue method worked perfect and I believe was it's original intent. To get back to the original value just call nonretainedObjectValue

Sean
Very interesting, thanks for that. I've unfortunately already gone down the other path, but in the future I will remember to give this a try.
craig