NSMutableDictionary. Using an object to store an integer value - objective-c

I'd like to add instances of my custom class to a NSMutableDictionary so that each instance has a corresponding integer value? I later want to be able to retrieve the integer value using the class instance as the key.

NS*Dictionary requires objects for both keys and values. If you want to shove an integer into a collection instance (dictionary, array, set, etc.) then you must "box" it first using NSNumber; [NSNumber numberWithInt:x];.
The keys to an NSMutableDictionary generally need to be copyable and must have a stable hash and stable isEqual: behavior (as per the documentation). Thus, your instances of your custom class must fulfill the NSCopying protocol and must properly support hash and isEqual:. hash and isEqual: may likely "just work" if pointer equality is good enough. Copying can be tricky.
In general, though, it is rare to have a map between instances and integral values. Why not just add an #property to your class?
i.e.
#property int magicNumber;
Far more straightforward and a heck of a lot more efficient, too.
Note, also, that if you need to associate something with an existing instance where you can't modify the class, you should use objc_setAssociatedObject() and objc_getAssociatedObject(). They don't require that the instance be NSCopyingable and are relatively efficient, too (though not as efficient as an #property).

You should be able to simply use [myMutableDict setObject: [NSNumber numberWithInt: myInt] forKey: myClassInstance];
At least thats what i read out of the fact that setObject takes (id) as parametertypes. :)

Related

isEqual of NSManagedObject - How does it determine equality?

I'm dealing with an NSMutableDictionary with NSManagedObject keys. Some of the data in my keys are going to be mutated, but the NSMutableDictionary class reference states that the keys are copied when using setObject! And when I want to access values using keys, it's going to compare the key I give it to the keys in the key list.
Sorry if this is a dumb question, but I can't find on the NSManagedObject class reference what isEqual does. Does it compare every value, compare some unique identifiers, compare the memory addresses, or something else?
I've seen some unsure answers here: Should I use == or [NSManagedObject isEqual:] to compare managed objects in the same context?, one of them saying that it compares the hash methods. Hash codes are not necessarily unique identifiers of objects, right?
I see others have answered in the comments - but for the "officiality" of it - here is the proper detailed answer.
Since NSManagedObjcet class does not conform to <NSCoding> protocol - it cannot serve as a key in NSDictionary or NSMutableDictionary.
Rather - you would want to use the objectID property of your managedObject as key in a dictionary. objectID is an NSString, which of course conforms to NSCoding.
Using objectID has another benefit. If you have more than one NSManagedObjectContext, The same entity will have a different instance of the NSManagedOBject for each NSManagedObjectContext. The only way to know they are one - is via their objectID property - which will be isEqual.

Objective-C HashMap equivalent

I am trying to convert a piece of Java code which uses a HashMap that contains as a key an object and a value as an object.
private static HashMap<Class<? extends Component>, ComponentType> componentTypes = new HashMap<Class<? extends Component>, ComponentType>();
I've been reading on how to do this with Obj-C but I have not been successful, most people suggest using a NSDictionary, the problem is that they keys need to be strings and I need them as objects. The other option was NSMapTable, however it is not available on iOS. Would someone be able to assist on how I can convert this into an obj-c equivalent?
thanks,
The keys for an NSDictionary do not need to be strings. They can be any object that implements NSCopying. If the object is a custom object, however, it needs to produce sane responses to the -hash and -isEqual: messages, but this is the same as using an object in a Java collection so it shouldn't be much of a challenge.
An NSMutableDictionary (assuming that you also need to set values in the dictionary after its initialization) works in two ways:
As a traditional dictionary/hashmap in which you set values like this:
[myDictionary setObject: theValue forKey: anyObject];
As an object with KVC-compliant properties that happen to be defined dynamically:
[myDictionary setValue: theValue forKey: aString];
If the key is an NSString, then the two are interchangeable, with the exception that you can't set an object to nil with setObject:forKey:, but you can pass nil to setValue:forKey:.
You want to use an NSDictionary. You say that
they keys need to be strings and I need them as objects
The keys to an NSDictionary don't need to be strings -- they can be any object that conforms to the NSCopying protocol.
From NSDictionary reference
A key-value pair within a dictionary is called an entry. Each entry consists of one object that represents the key and a second object that is that key’s value. Within a dictionary, the keys are unique. That is, no two keys in a single dictionary are equal (as determined by isEqual:). In general, a key can be any object (provided that it conforms to the NSCopying protocol—see below), but note that when using key-value coding the key must be a string (see “Key-Value Coding Fundamentals”). Neither a key nor a value can be nil; if you need to represent a null value in a dictionary, you should use NSNull.
So any object that meets the NSCopying protocol can be used as a key. The string restriction is only for Key-Value Coding used for Cocoa bindings
I'm inferring that you are using a key that is does not conform to the NSCopying Protocol. In that case try using the Core Foundation equivalent of NSDictionary: CFDictionary.
http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFDictionaryRef/Reference/reference.html
Just make sure that when you are using CFDictionary that all of your objects are going to be retained in memory for the duration of the object. Since CFDictionary has to be set with weak references (at least in my experience) just be careful that you don't accidentally dealloc one of your objects whiles it's still in the CFDictionary.
While CFDictionary is “toll-free bridged” with its Cocoa Foundation counterpart, NSDictionary, I find that there are still problems with this. I've tried to add objects that were not part of the NSCopying protocol to my toll-free bridged NSDictionary and it came up with an error at run time.
CFDictionary docs: http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFDictionaryRef/Reference/reference.html
If you need mutability, use CFMutableDictionary instead like so:
cfd = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
CFDictionaryAddValue(cfd, key, value);
CFRelease(cfd);
CFMutableDictionary docs: http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFMutableDictionaryRef/Reference/reference.html#//apple_ref/doc/uid/20001497

Object keys with NSMutableDictionary (Objective-C)

I want to store a bunch of key value pairs, with the key being my own object (ObjectA) that inherits from NSObject, and the value being an int.
I am trying to use an NSMutableDictionary. I understand that you can only store object types in the dictionary, so I have the following:
id value = [NSNumber numberWithInt:my_number];
[[self dictionary] setObject:value forKey:myObjectA];
Now that gives me an error, saying
-[ObjectA copyWithZone:]: unrecognized selector sent to instance
which is fine, I understand that object keys need to implement the NSCopying protocol. However I then read that you can do this by wrapping your objects using NSValue.
Can someone please explain how I would wrap my objects, and how I can then find the value by the key? Am I still able to use dictionary objectForKey:myObjectA or do I have to wrap myObjectA with an NSValue object while I'm searching as well? Or should I be implementing NSCopying on my custom class, or using a string key instead?
I am looking for this simplest and easiest way to use a dictionary, if I have to I'll implement a string key and use setValue:forKey: instead but I'd rather use the object key if I can.
Dictionary keys are always copied. So you simply need to implement the NSCopying protocol for your class, which is just the copyWithZone: method.
Additionally you should implement the isEqual: method for your class.
Edit: How to implement your copyWithZone: depends on a number of factors (main factor: deep vs. shallow copy). See Apple's Implementing Object Copy guide and this SO answer.
You could turn an id into an NSValue with:
NSValue* value = [NSValue valueWithNonretainedObject:object];
...
id object_ = [value nonretainedObjectValue];
but you need to manage the ownership outside of the dictionary. This is going to be a mess. It's better to adopt NSCopying.
There is also a 4th option: use a CFDictionary, which allows the object only can be CFRetain/CFReleased, not copied.
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks
);
...
CFDictionarySetValue(dict, myObjectA, value);
...
CFRelease(dict);
And if you're programming for Mac or iOS 6 and above, try NSMapTable.
NSMapTable* dict = [[NSMapTable mapTableWithStrongToStrongObjects] retain];
...
[dict setObject:#"?" forKey:foo];
...
[dict release];
In iOS 6 you can use NSMapTable (https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/NSMapTable_class/Reference/NSMapTable.html), which allows you to chose weak/strong attributes for the keys and objects.
You don't need to wrap your object using NSValue. What you have will work except you're missing a piece. For myObjectA's class you need to adopt the NSCopying protocol (see the docs for what to add). Once that's added the code you posted above should work correctly.
You might want to consider using strings though over your own object for the key. The key is required to be a string if key-value coding is going to be used to access it at all. So using a string will make life easier if you can take advantage of key-value coding anywhere you're using the dictionary.

What is the difference between valueforKey:, objectForKey:, and valueForKeyPath:?

I have 2 questions:
What is the difference between valueForKey: and objectForKey:? Is it that one is for NSDictionarys (objectForKey:) and for others it is valueforKey:, or is it the reverse?
Also what is the difference between valueForKey: and valueForKeyPath:? Has it got something to do with Core Data?
Please help.
valueForKey: is part of the NSKeyValueCoding protocol and is therefore part of the key-value coding framework, which allows you to access class properties by name at runtime. That's how NIBs are loaded, for example — the names of properties associated with connections are loaded and then the values are set directly by name. This contrasts with the way that visual interface design tools often work in other languages, generating lots of hidden statically compiled code.
objectForKey: is defined on dictionaries only, and looks up an object by its key. An NSDictionary is a class that stores connections between values and keys.
So, valueForKey: could be used on an NSDictionary to return meta information about the dictionary, such as the count of objects inside it, the list of all keys, etc. objectForKey: would be used actually to look into the dictionary.
At runtime, the difference is that objectForKey: is a method with a completely opaque implementation. valueForKey: explicitly relies on subsequently calling named getters and setters. The reason for the latter is that you can extend key-value coding to key-value observing, where you ask to be informed every time a particular property on a particular object changes. At runtime that's achieved with a method swizzle, where the original setter is replaced by a new one that calls the previous setter and then sends out the required messages. Because all messages are dispatched dynamically, that's just achieved by modifying tables within the object instance.
So any object that is key-value coding compliant (which just means declaring and implementing your properties in the proper way, which the new-ish #property/#synthesize syntax does automatically) can be observed without the object itself having to implement any code.
There's further Apple stuff that uses key-value coding to achieve various things, including CoreData, but it's not specifically to enable any one other technology.
valueForKeyPath: is like valueForKey: except that it can traverse several objects. So you can have a root object with a bunch of properties, each of those properties is another object with another bunch of properties, etc, and using a key path you can retrieve a value way out at the leaf of that data structure rather than having to iterate through object after object for yourself.
In summary, valueForKey: and valueForKeyPath: provide information about object instances and interact with the dynamic nature of the Objective-C runtime. objectForKey: is a dictionary specific method that does dictionary tasks.
Additions:
An example, coded as I type and assuming that NSDictionary is key-value coding compliant:
NSDictionary *someDictionary;
// create someDictionary, populate it, for example (note: we assume photoOfKeys.jpg
// definitely exists, not a good idea for production code — if it doesn't we'll get
// a nil there and anything after it won't be added to the dictionary as it'll appear
// that we terminated the list):
someDictionary = #{ #"favouriteGarment": #"hat",
#"#allKeys" : [NSImage imageNamed:NSImageNameDotMac],
#(2) : NSArray.new };
NSObject *allKeys;
// we make no assumptions about which type #allKeys will be, but are going to assume
// we can NSLog it, so it needs to be a descendant of NSObject rather than 'id' so as
// to definitely respond to the 'description' message — actually this is just compile
// time semantics, but if someone else reads this code it'll make it obvious to them
// what we were thinking...
// some code to get all of the keys stored in the dictionary and print them out;
// should print an array containing the strings 'favouriteGarment', '#allKeys' and
// the number 2
allKeys = [someDictionary valueForKey:#"#allKeys"];
NSLog(#"%#", allKeys);
// some code to get the object named '#allKeys' from the dictionary; will print
// a description of the image created by loading photoOfKeys.jpg, above
allKeys = [someDictionary objectForKey:#"#allKeys"];
NSLog(#"%#", allKeys);
// `objectForKey is analogous to `objectForKeyedSubscript:`, aka
allKeys = someDictionary[#"#allKeys"];
allKeys is a property of NSDictionary as described here. I've also added a mapping from the NSString allKeys to a photograph of some keys. Whether I use the key-value coding valueForKey: methods or the NSDictionary objectForKey: lookup method dictates whether I read the property of the object instance or whether I send the object instance a message asking it to do its unique job.
objectForKey: is a method on NSDictionary for accessing the object associated with a key. valueForKey: is a method on NSObject for accessing any value associated with any object, through the name of a accessor method, property, and/or instance variable.
valueForKeyPath: can be seen as a shorthand for several calls to valueForKey:. You can think of it as sort of a xpath, if you will.
These two statements will result in the same output:
// Using nested valueForKey:
NSLog(#"%#", [[myObject valueForKey:#"foo"] valueForKey:#"bar"]);
// Can be done with a single valueForKeyPath;
NSLog(#"%#", [myObject valueForKeyPath:#"foo.bar"]);
valueForKey:and valueForKeyPath: are part of KVC (Key Value Coding). Introduction and in-depth documentation can be found here: http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/
valueForKey: and valueAtKeyPath: are methods defined in the NSKeyValueCoding informal protocol, and default implementations for both are provided by the root class NSObject.
objectForKey: is a method on NSDictionary.
valueForKey: takes a key to a property, while valueAtKeyPath: takes a so-called keypath. A keypath is a period-delimeted path to a specific property, like #"relationship.property".

Object pointer value as key into dictionary

I want to use the object's reference value as a key into a dictionary, as opposed to a copy of value of the object. So, I essentially want to store an object associated with a particular instance of another object in a dictionary and retrieve that value later.
Is this possible? Is it completely against the idea of NSDictionary? I can tell that I am probably approaching this the wrong way because the dictionary wants me to implement NSCopying on the object itself, which doesn't really make sense in terms of what I'm doing. I can see that what I should really be doing is wrapping the pointer value, but that seems a little mad.
Advice would be appreciated.
I think you can use [NSValue valueWithPointer:object].
NSMutableDictionary has been designed to only deal with Objective-C object instances. For example, when you call setObject:forKey: method calls copyWithZone: on the key and retain on the value.
If you want to have a dictionary structure and to be able to deal with arbitrary key and value, then you can go with CFMutableDictionary. You can describe precisely what is done with key and values; it is flexible enough to deal with arbitrary pointer or event char * strings.
This did the trick for me
aDictionary[#((intptr_t)object)] = ...;
You can use the address in memory of myObejct as a key in myDictionary
NSString *myObject_addressInMemory = [NSString stringWithFormat:#"%p", myObject];
myDictionary[myObject_addressInMemory] = someValue;