Using NSDictionaries - iphone

I have a number of NSString objects I want to save into an NSDictionary.
How is an NSDictionary initialized with the keys and do the values need to be set there and then or can the dictionary be setup using just the keys and the objects later set?

To start with here's a link to the NSDictionary documentation, and NSMutableDictionary
You can create a NSMutableDictionary with just keys by passing [NSNull null] as the object for that key. It's important to use a mutable dictionary here otherwise you won't be able to change the dictionary after you've created it, and a dictionary that just has keys instead of objects is of little use.
That is what NSNull is for: to provide null values for collection types that don't allow nils.

A dictionary can be initialized both as empty or as a list of (value,key) pairs.
Initializing the dictionary just with keys and not values is useless/meaningless in Objective-C, since it will be equivalent to a void initialization.
If you want to create a dictionary and want to be able to set objects later, you have to use a NSMutableDictionary, that has not a static initialization of its data, through its setObject:forKey: method.

Related

Accessing array/set of objects with keys

I want to be able to add objects to an NSArray and access them with Keys. Currently the way im doing it is creating a seperate NSDictionary of key-value pairs where the value is an integer number representing the index in my NSArray. This seems like an extra step to me.
If my understanding of NSDictionary is correct, only 'values' can be stored: a pointer to an object cannot.
Surely there must be an equivalent NSDictionary type function that allows objects to be stored and accessed with a key? I have looked through the documentation, but cant seem to find any answers, unless im missing something obvious.
NSDictionary is to store key value pairs. if you are adding key value pair after you created the dictioanry, use NSMutableDictionary class . example,
[dictionaryObject setObject:#"" forKey:#"abc"];
You can store objects in NSDictionary and can be accessed via keys...
In short, no.
An array (NSArray) is an ordered collection of references to objects, so simply said, an ordered collection of objects.
As opposed to dictionaries, which are unordered and values are accessed by keys.
You understanding of collections is probably wrong, you don't store values, but pointers (references).
The extra step is necessary if you need to store the references in an array, but in this case, you should consider using a dictionary. An option is to use keys that take care of the order.
For example :
[myDictionary objectForKey:#"1"];
could be an equivalent of :
[myArray objectAtIndex:1];
Thats wrong, you can store objects in a NSDictionary. Look at the method dictionaryWithObjects:forKeys: or dictionaryWithObjectsAndKeys:
I have no experience in Cocoa but looking at the documentation it seems like NSDictionary (or at least NSMutableDictionary) should handle your request (without you using NSArray).
I think I understand your problem. My suggestion for you is to use NSMutableArray and macros, like:
NSMutableArray *array=[[NSMutableArray alloc]init];
#define SOME_MACRO 0
id someObject;
[array insertObject:someObject atIndex:SOME_MACRO];
id getterObject=[array objectAtIndex:SOME_MACRO];
Of course define the macros in the header file.

How to use an object as a key in Objective-C

I would like to use a custom object as a key in a hash-like structure. I've tried using NS[Mutable]Dictionary but in order for my object to be a key it has to implement the NSCopying protocol. NSDictionary is sending a copy message to all of it's keys as far as I've read. I don't want to implement the protocol (my object is quite complex) nor do I want it to be copied. What are my options? Do I have any?
NSDictionary is toll-free bridged with CFDictionaryRef, but they actually differ in behavior when adding objects. Specifically, NSDictionary's -setObject:forKey: will copy the key, but CFDictionaryRef's CFDictionarySetValue() will not copy the key. This means that if you want to use non-copyable keys, you can use CFDictionarySetValue() instead to add it to the dictionary.
CFDictionarySetValue((CFMutableDictionaryRef)myDict, myKey, myValue);
This will still retain the key, but it won't copy it. And you can use the normal NSDictionary methods for everything else.
Do you need the NSDictionary to retain the object? If not, you can turn it into an NSValue and use that as the key:
NSValue *value = [NSValue valueWithNonretainedObject:yourCustomObject];
[dictionary setObject:someObject forKey:value];
This can get a bit messy but is in alternative to implementing NSCopying.
You can roll your own dictionary. Not really that hard.
Another option is to use a surrogate object, containing a pointer to "the" object. The surrogate would implement the hash and either copy or reference the fields to be compared for isEqual. It could do a basic sanity check to assure the compared fields have not been changed when it's referenced.
You could just do this:
- (id)copyWithZone:(NSZone *)zone {
return [self retain];
}

iPhone - Changing a sub-sub-sub NSMutableDictionary value

If I have a data tree that is something like :
NSMutableDictionary (dict1)
NSMutableDictionary (dict2)
NSMutableArray (array)
NSMutableDictionary (dict3)
Key1
Key2
NSMutableDictionary (dict_n)
Keyn
Keyn
NSMutableDictionary (dict_n)
NSMutableArray (array_n)
NSMutableDictionary (dict_n)
Keyn
Keyn
NSMutableDictionary (dict_n)
Keyn
Keyn
If I want to change the value of Key1, is there a simplier way than...
getting dict1
then getting dict2
then getting array
then getting dict3
converting dict3 to a mutable Dictionary
then setting key1
Converting array to a mutable array
then setting dict3 into array
converting dict2 to a mutable Dictionary
then setting array into dict2
converting dict1 to a mutable Dictionary
then setting dict2 into dict1
Doing that for each value I have to change is a real headache, and really code consuming.
You can't send a message to an object unless you have a pointer to that object, so in a basic sense the answer to your question is no, there's no other way.
However, one presumes that this data structure that you have represents some sort of data model. As such, it should probably be contained in some sort of model class that understands its parts, and that class should be the only one that needs to understand how the data is stored. The model class should offer a higher-level interface to the data. Say dict3 represents one particular vehicle in a fleet, and the keys are things like "vehicleTag", "registrationDate", "purchaseDate", etc. Maybe the dictionaries at the dict2 level the fleets in different regions, and dict2 itself represents the northeast fleet. Then the VehiclePool class, your model class which stores all the data, might offer methods like:
-registrationDateForVehicle:(int)vehicleIndex inFleet:(NSString*)fleetKey;
-setRegistrationDate:(NSDate*)regDate forVehicle:(int)vehicleIndex inFleet:(NSString*)fleetKey;
I'm not sure I'd really want an API like that -- I'd prefer to get the list of vehicles for a given fleet and then operate on those with simpler accessors, but you seem to want to avoid several levels of accessors. The point here is that you shouldn't be writing a ton of code to do the operations you need; you should write methods that know how to access the data and then call those.
The NSDictionary documentation states:
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”).
Therefore, if your keys (and sub-keys) are all strings, you can do the following to retrieve and set values:
id myNestedObject = [topLevel valueForKeyPath:#"firstKey.secondKey.thirdKey"];
[topLevel setObject:newNestedObject forKeyPath:#"firstKey.secondKey.thirdKey"];

problem in nsuserdefaults

hii every one i have stil problem in nsuser defaults i'll tell the scenerio in detail
First i have diclared nsmutable array in appDelegate and set it in NSUserDefaults With For Key#"abc"
In FirstView Controller i first fetch the array from NSUserDefaults and save its values in NSMutable Array
When a Click say abcButton i have create a dictionary and adding values in it like
[abcDictionar setObject:[[abcMutableArray objectAtIndex:indexPath.row] objectForKey:#"abc"] forKey:#"abc"];
When i added all values in NSDictionary Then i add NSDictionary in NSMutable Array
like This
[abcMutableArray addObject:abcDictionary];
Then i save it NSUserDefaults
It Give me Exception in Point 4
When i add nsdictionary in Point One It All work fine but data in array is ambigous and it raise exception when am going to display it in tableview
if any one has some idea then let me know thanks in advance...:)
NSUserDefaults does not store mutable objects, only immutable ones. When you retrieve objects from it you must cast/copy them into mutable objects if you wish to mutate them.
Also be aware that:
The NSUserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Booleans, and URLs. A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData.
Which is from the NSUserDefaults documentation.

Application crash problem in iPhone

I have used NSMutable Dictionary and NSMutable Array. The datas are to be stored and retrieved from plist(Documents Directory) using NSArray of NSMutable Dictionary.
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object'
Please Guide me why its happened?.
Thanks!
It might help if you post the exact code that causes. My guess would be that while you are using NSMutableDictionary, the call to valueForKey: returns to you a non-mutable NSArray, and you think it is returning you an NSMutableArray instance. Note that mutable arrays and dictionaries allow you to manipulate the collection of items inside them, but do not guarantee you that those items themselves are mutable. For example, if you check the Property List Programming Guide: Reading and Writing Property-List Data, you will notice the following example:
If you load the property list with
this call:
NSMutableArray * ma = [NSMutableArray arrayWithContentsOfFile:xmlFile];
ma is a mutable array with immutable
dictionaries in each element. Each key
and each value in each dictionary are
immutable.
If you need explicit control over the mutability of the objects at each level, use propertyListFromData:mutabilityOption:format:errorDescription:
You can also create an explicit NSMutableArray copy from the NSArray you got from the NSMutableDictionary.
you said you are retrieving the array as a NSArray and not casting it to a NSMutableArray before you attempt to remove an object for it. This causes an error since you can't remove an object from an NSArray
Suppose [something dictionaryValue] returns an immutable dictionary, and you want a mutable version of that dictionary. It is not enough to say:
NSMutableDictionary *d = [something dictionaryValue];
This merely tells the compiler that d is an NSMutableDictionary, but really it's the same immutable dictionary you got from [something dictionaryValue]. Instead, you need to create a new, mutable copy of the dictionary:
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary:
[something dictionaryValue]];
Similarly, use [NSMutableArray arrayWithArray:...] for arrays.