Using class as key in NSDictionary - objective-c

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.

You're using setValue:forKey: which only takes NSStrings as keys. you should be using setObject:forKey: instead. A class object (pointers to class objects can be passed as type Class) is a full-fledged Objective-C object (a class object is an instance of its meta-class, and you can use all the NSObject methods on a class object; read more about meta-classes here), so they can be used anywhere objects are used.
Another requirement for keys of a dictionary is that they support copying (i.e. have the copyWithZone: method. Do class objects support this method? In fact, it does. The NSObject class defines a class method +copyWithZone:, whose documentation explicitly says that it "lets you use a class object as a key to an NSDictionary object". I think that's the answer to your question.

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

While a Class object makes a perfectly good key in an NSDictionary, it's worth mentioning NSMapTable, which is modeled after NSDictionary, but provides more flexibility as to what kind of objects are suitable for use as keys and/or values, documented to support weak references and arbitrary pointers.

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

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.

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.]

You can use classes as NSDictionary's keys like this:
#{
(id)[MyClass1 class] : #1,
(id)[MyClass2 class] : #2,
(id)[MyClass3 class] : #3,
(id)[MyClass4 class] : #4,
};
Or even like this:
#{
MyClass1.self : #1,
MyClass2.self : #2,
MyClass3.self : #3,
MyClass4.self : #4,
};

Related

Unable to use 'class' as a key in NSDictionary

I'm trying to use a class as a key in an NSDictionary. I looked at the answer to this question and what I have is pretty much the same; I'm using setObject: forKey:. However, XCode complains, saying Incompatible pointer types sending 'Class' to parameter of type 'id<NSCopying>'. The call I have is:
[_bugTypeToSerializerDictionary setObject: bugToStringSerializer
forKey: [bugToStringSerializer serializedObjectType]];
bugToStringSerializer is an instance of BugToStringSerializer whose concrete implementations implement serializedObjectType. An example of a concrete implementation looks like this:
- (Class) serializedObjectType {
return [InfectableBug class];
}
What am I doing wrong here?
(It seems that classes do conform to NSCopying, however their type is not id <NSCopying>.) Edit: classes do not conform to protocols. Of course the essential is that classes respond to the copy and copyWithZone: messages (and that's why you can safely ignore the warning in this case). Their type is still not id <NSCopying>.) That's why the compiler complains.
If you really don't want that ugly warning, just perform an explicit type conversion:
[dictionary setObject:object forKey:(id <NSCopying>)someClass];
Aha,I just fixed the bug in my project.
use this:
NSStringFromClass([Someclass class]);
The other answers are certainly helpful, but in this case it probably makes more sense to just use an NSMapTable, which does not copy the key unlike NSDictionary, and just retains it with a strong pointer (by default, although this can be changed).
Then you can just use your original code with no modifications.
NSMapTable *_bugTypeToSerializerDictionary = [NSMapTable new];
...
[_bugTypeToSerializerDictionary setObject: bugToStringSerializer
forKey: [bugToStringSerializer serializedObjectType]];
It's less hacky, and is clearer at conveying programmer intent.
For extra style points you could give the instance variable a slightly more fitting name like_bugTypeToSerializerMap.
This is my usual code:
#{
(id)[MyClass1 class] : #1,
(id)[MyClass2 class] : #2,
(id)[MyClass3 class] : #3,
(id)[MyClass4 class] : #4,
};
But recently I've discovered this approach:
#{
MyClass1.self : #1,
MyClass2.self : #2,
MyClass3.self : #3,
MyClass4.self : #4,
};

Missing sentinel in method dispatch

I want to create a subclass of NSMutableArray and need to override the -initWithObjects: method.
But How to call [super xxx];?
- (id) initWithObjects:(id)firstObj, ... {
[super initWithObjects:firstObj]; // Error: Missing sentinel in method dispatch
// Error: The result of a delegate init call must be immediately returned or assigned to "self"
}
Thanks.
Then "missing sentinel" message refers to the missing nil termination. In fact, according to font-of-all-knowledge-Wikipedia:
The name of the nil that terminates a variable length list of parameters in Objective-C
also: Sentinel node, an object to represent the end of a data structure
also: Sentinel value, a value used to terminate a loop
also: In network protocols such as Bisync, sentinel values indicate where frames start and end
You can't. As discussed in the documentation for NSArray:
You might want to implement an initializer for your subclass that is
suited to the backing store that the subclass is managing. The NSArray
class does not have a designated initializer, so your initializer need
only invoke the init method of super. The NSArray class adopts the
NSCopying, NSMutableCopying, and NSCoding protocols; if you want
instances of your own custom subclass created from copying or coding,
override the methods in these protocols.
So you can assign self = [super init]; and add the objects from your initialiser to the resulting object. Indeed, because of the way that NSArray is implemented, calling any -initWith… method is likely to return an instance of a different NSArray subclass.
Notice that the documentation also discusses alternatives to subclassing NSArray that may be easier, more reliable or better in some other way.
Subclassing NSArray/NSMutableArray doesn't work like subclassing most classes. NSArray is a class cluster, please see subclassing notes from the NSArray documentation.
Now, for your specific question, subclassing va_list methods is a bit tricky, there are a number of ways to handle this. The 'safest' would be to process your va_list into an NSArray and pass that into another method that dealt with whatever you wanted. The other, slightly less portable, slightly hackier way is to create a new va_list list on the stack to pass through.
id __unsafe_unretained * stack = (typeof(stack))calloc(numOfObjects, sizeof(id));
//filloutStack
[super initWithObjects:*stack, nil];
free(stack);
Subclassing Apples Collection classes isn't that difficult — if you use a tiny trick (see also: cocoawithlove).
A subclass is a "is-a" relationship in object-orientated Design. But there are also "has-a" relationships, i.e. wrappers.
If you would try to create a subclass of NSArray by using a pure is-a relationship, I guess, it would be kind of hard, as you would have to do C-level memory management.
But if you add a has-a relationship — or: create a wrapper — at the same time, you can the subcalssing quite easily: Just make your custom array class have a member of a regular NSArray. Now override its method by forwarding the calls to the member object. I showed this in this post, where I just add objects, that pass a certain test.
But you will see, that I didn't implement the method you talked about correctly, but I raise a error. The reason is: that method is a variadic methods, that has a variable number of objects you can pass in — and to handle this, you have to to a bit of work. cocoawithlove has an great article about it.
For you — if using that has-a trick — it could look like
- (id) initWithObjects:(id)firstObj, ... {
if (self = [super init]) {
_realArray = [[NSMutableArray alloc] initWithCapacity:1];
}
va_list args;
va_start(args, firstObj);
for (id obj = firstObj; obj != nil; obj = va_arg(args, id))
{
[self.realArray addObject:obj];
}
va_end(args);
return self;
}
Try
self = [super initWithObjects:firstObj,nil];

Help me understand why: [self propertyName] works while propertyName doesn't

So I had a class defined with a property - we'll call it propertyName for the sake of this example. I had the property setup with #synthesize in my implementation.
I have a method called objectToNSDictionary which basically dumps that property into a dictionary:
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:
[self propertyName], #"propertyName", nil];
I return that dict to the caller I use a JSonWriter to convert it to a string and pass it off to some service...
Suffice it to say that the above works. However, my original implementation didn't use [self propertyName] but instead just used propertyName. When I did that, I always had an error saying unrecognized selector sent to instance when I tried to use the object in the caller.
What's the difference in syntax really saying and why does one work and not the other?
When you use [self propertyName] you are referencing the property you defined for your class, via the synthesized getter method. When you use propertyName directly you are bypassing the property and using the class ivar directly. This will work as long as your ivar really is called propertyName, which is not required and might not be the case. Generally its a bad idea to access your ivar directly because doing so circumvents the memory management scaffolding that the compiler generates for you.
You need to post relevant code from your calling class to be able to tell why you are getting a 'selector not recognized' message.

Objective-C pattern for class instance variables?

What would be a nice pattern in Objective-C for class variables that can be "overridden" by subclasses?
Regular Class variables are usually simulated in Objective-C using a file-local static variables together with exposed accessors defined as Class methods.
However, this, as any Class variables, means the value is shared between the class and all its subclasses. Sometimes, it's interesting for the subclass to change the value for itself only. This is typically the case when Class variables are used for configuration.
Here is an example: in some iOS App, I have many objects of a given common abstract superclass (Annotation) that come in a number of concrete variations (subclasses). All annotations are represented graphically with a label, and the label color must reflect the specific kind (subclass) of its annotation. So all Foo annotations must have a green label, and all Bar annotations must have a blue label. Storing the label color in each instance would be wasteful (and in reality, perhaps impossible as I have many objects, and actual configuration data - common to each instance - is far larger than a single color).
At runtime, the user could decide that all Foo annotations now will have a red label. And so on.
Since in Objective-C, Classes are actual objects, this calls for storing the Foo label color in the Foo class object. But is that even possible? What would be a good pattern for this kind of things? Of course, it's possible to define some sort of global dictionary mapping the class to its configuration value, but that would be kind of ugly.
Of course, it's possible to define some sort of global dictionary mapping the class to its configuration value, but that would be kind of ugly.
Why do you think this would be ugly? It is a very simple approach since you can use [self className] as the key in the dictionary. It is also easy to make it persistent since you can simply store the dictionary in NSUserDefaults (as long as it contains only property-list objects). You could also have each class default to its superclass's values by calling the superclass method until you find a class with a value.
+ (id)classConfigurationForKey:(NSString *)key {
if(_configurationDict == nil) [self loadConfigurations]; // Gets stored values
Class c = [self class];
id value = nil;
while(value == nil) {
NSDictionary *classConfig = [_configurationDict objectForKey:[c className]];
if(classConfig) {
value = [classConfig objectForKey:key];
}
c = [c superclass];
}
return value;
}
+ (void)setClassConfiguration:(id)value forKey:(NSString *)key {
if(_configurationDict == nil) [self loadConfigurations]; // Gets stored values
NSMutableDictionary *classConfig = [_configurationDict objectForKey:[self className]];
if(classConfig == nil) {
classConfig = [NSMutableDictionary dictionary];
[_configurationDict setObject:classConfig forKey:[self className]];
}
[classConfig setObject:value forKey:key];
}
This implementation provides no checking to make sure you don't go over the top superclass, so you will need to ensure that there is a value for that class to avoid an infinite loop.
If you want to store objects which can't be stored in a property list, you can use a method to convert back and forth when you access the dictionary. Here is an example for accessing the labelColor property, which is a UIColor object.
+ (UIColor *)classLabelColor {
NSData *data = [self classConfigurationForKey:#"labelColor"];
return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
+ (void)setClassLabelColor:(UIColor *)color {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:color];
[self setClassConfiguration:data forKey:#"labelColor"];
}
my answer here may help:
What is the recommended method of styling an iOS app?
in that case, your annotation just holds a reference to a style (e.g. you need only one per style), and the size of a pointer for an entire style is not bad. either way, that post may give you some ideas.
Update
Jean-Denis Muys: That addresses the sample use case of my question, but not my question itself (a pattern to simulate class instance variables).
you're right, i didn't know how closely your example modeled your problem and i considered commenting on that.
for a more general and reusable solution, i'd probably just write a threadsafe global dictionary if your global data is nontrivial (as you mentioned in your OP). you could either populate it in +initialize or lazily by introducing a class method. then you could add a few categories to NSObject to access and mutate the static data -- do this for syntactical ease.
i suppose the good thing about that approach is that you can reuse it in any program (even though it may appear ugly or complex to write). if that's too much locking, then you may want to divide dictionaries by prefixes or create a simple thread safe dictionary which your class holds a reference to -- you can then synthesize an instance variable via the objc runtime to store it and declare an instance method to access it. the class method would still have to use the global data interface directly.

Is it best to return NSArray or void and update self property?

I am working on a delegate class that controls several views, and find myself switching between updating properties in the delegate and returning values from methods. What is the proper way to do this?
-(NSArray)blah{
return myarray;
}
or
-(void)blah{
[self myarray:value]
}
--------------- Clarification of question below
if I have a helper method that converts an NSArray into a NSDictionary
should I call my helper method and expect a return of NSDictionary, or should I update a variable in memory and return void.
There's a case for each approach, depending on what you are really doing. The two choices are:
It is truly a helper method, that has use in many places in your application.
It is specific to a single class and the dictionary is a member of that class.
OPTION 1) If it is truly a helper method, I believe that you should return the NSDictionary from the method. I'm assuming it is newly allocated within that method.
In other words, prefer:
+ (NSDictionary *) dictFromArray:(NSArray *);
If it has utility outside of a single class, you could put it in a sensible class that collects related utility methods.
The alternative approach of passing in an empty dictionary to be filled is practiced in C because it creates symmetry around allocating and freeing and makes it clear who owns the memory.
In Objective-C, reference counting takes care of that, so you can avoid the extra code of allocating empty objects just to call the method.
For example:
NSMutableDictionary *myDict = [[NSMutableDictionary alloc] init];
dictFromArray(myArray, myDict);
When it comes to knowing who owns the object, you should stick to Objective-C conventions, where:
+ (NSDictionary *) dictFromArray:(NSArray *)array
returns an autorelease object, so the caller knows they need to retain it if they want to hold a reference.
OPTION 2) If the functionality is specific to a single class and that class has the dictionary as a member, then I would pass in the array, update the dictionary member variable using the array contents, and return void.
Something like:
- (void) setBlahFromArray:(NSArray *)array
The question is confusing as stated. If they are properties then you have accessor methods that usually include something like:
-(void) setMyValue: (NSString*) inNewValue;
-(NSString*) myValue;
but it seems like you are probably asking something else since these can be dynamically synthesized for you by the compiler... So try rephrasing the question and we'll try again to help.