self referencing NSMutableDictionary - ios

Say I have this:
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[#1] = #2;
dict[#3] = dict;
I archive dict by calling:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:dict];
then I unarchive later:
NSMutableDictionary *dict2 = [NSKeyedUnarchiver unarchiveObjectWithData:data]
The problem is that dict2[#3] is not dict2, but rather an uninitialized NSDictionary, and I was not able to recover what I had put in. Does anyone know how I would work around this? Thanks in advance!

It is explained by an Apple engineer on the Apple Mailing List: http://lists.apple.com/archives/cocoa-dev/2007/May/msg00747.html,
in reply to a similar question about archiving an NSMutableArray
containing itself as an element.
Summary:
This (very probably -- I haven't looked into it) is not a problem with
the recursion per-se, but rather with objects which replace themselves
during unarchiving by returning a new object from initWithCoder:.
...
So the summary answer is: you can't reliably have recursive
references. In practice, though, these do occur (think of the NSView
subview/superview relationship) and things squeak by. Sometimes they
don't and it's impossible to know beforehand if something will work or
not.
Chris Kane
Cocoa Frameworks, Apple

I would be really surprised if this worked. When you set dict[#3]=dict, you are basically giving it an infinite loop to create an infinitely deep dictionary. When you create data from the dictionary, it seems to protect you from that by replacing the infinite dictionary with an uninitialized one so that the user's ram is not completely drained before the application crashes.
If you were to try to print out dict[#3] in the console, the application would infinitely loop trying to print it out and would get stuck until it finally crashes sometime later.
Hence, f you want to store your dictionary in a dictionary, create another one.

Related

iOS read data from a bit more 'complex' .plist

I know this question had been asked for several times, but I have trouble reading data in Xcode iOS. The problem was to achieve an implementation without having the keys for NSDictionaries hardcoded in the code.
Let's say, we have this .plist:
This is my plist
In this case I want to access the name and address from the restaurants dynamically without accessing the data by a hardcoded string.
Background of this is that I try to access the data on every cell on a grouped UITableView for iOS.
Anyone has some good information about this?
Generally (normally I code in c#) I know how to access dictionaries. But in this case, there are nested dictionaries with arrays which get me really confused and also frustrating :(
NSString *path = [[NSBundle mainBundle] pathForResource:#"PropertyListName" ofType:#"plist"];
//Plist should exist in main bundle
NSDictionary *root = [[NSDictionary alloc] initWithContentsOfFile:path];
Then in your case your first value is a array in dictionary read that object in an array like,
NSArray * restaurantsArray = [names objectForKey:#"restaurants"];
Then so forth You know what type of object exist in the hierarchy just call respective methods of each,
like objectForKey for dictionary.
objectAtIndex for array.

How to maintain the order of key added in NSMutableDictionary

I didn't knew this at first place, I thought that the key/value pair I am adding to my dictionary will be in the same order when I try to retrieve it back...
Now here is my question -
I have a ViewController which I have made as singleton, inside this VC I have defined:
#property (nonatomic) NSMutableDictionary *dictionary;
Now I try accessing this dictionary from various other classes and set its contents via:
[[[ViewController sharedViewController] dictionary] setValue:[NSNumber numberWithBOOL:NO] forKey:#"xx"] , yy ,zz and so on
I have my delegates implemented which would hit for a particular key(xx,yy,zz and so on). Now whenever a particular delegate method is hit I need to update my dictionary with same key but [NSNumber numberWithBOOL:YES] which is happening .. but the order in which I added the items in dictionary is not maintained ... How can I achieve this?? I need to maintain the order in the same way in which I have added my items to the dictionary at first place??
Can someone please help me out??
As pointed, you should use NSArray to have your values ordered. You can also use some 3rd party APIs like: M13OrderedDictionary or others, however, that's not a pretty solution.
Obviously, the desired solution is to create your own object or struct and keep an array of those objects/structs.
NSDictionary is just a mapping between keys and values that has no defined order. If you try to iterate over the keys, you may or may not get them back in the same order you added them, and you should never depend on the ordering of the keys when iterating over them.
One thing you could do is create an NSArray that contains the keys in the order you want them. Then you can iterate over the keys in the array and get the values from the NSDictionary in the order you wanted. The combination of these two basically gives you a sorted key NSDictionary.
Another thing you could do is to just use an NSArray to stores the values, unless the keys that you're using matter as well. But if all you're trying to do is get the values in a certain order, then I would say NSArray should work fine.
You can make a copy of key by using the following code:
NSArray *allKeys = [dictionary allKeys];
Then, the keys all saved in order and you can access particular value by getting specific key in allKeys array and retrieving it by using dictionary[allKeys[2]].

objectAtIndex for Nested Array iOS

Beginners iOS dev question here.
I have a plist in the following format:
I've converted this plist into an array in my app using:
NSMutableArray *array2 = [[NSMutableArray alloc] initWithContentsOfFile:path];
and have verified the content using NSLog.
However the issue I have is in understanding objectAtIndex. How do i obtain Item2 in Item1 i.e. the value 6? I'm used to Javas "array[1][2]" style :p
You can actually still use array[1][2] in Objective-C (with Xcode 4). Otherwise the "Objective-C way" to do it is:
[[array objectAtIndex:1] objectAtIndex:2];
But it is lots more code and for someone from most other backgrounds, not as readable either.
It was quite tedious, but Apple have provided a simple way to do it now. Simply put, it would be:
array2[1][2];
The first number inside the square brackets sends -[array2 objectAtIndex: 1] to the top level array. The second number sends the inner array the same message with 2 as an argument.

Create a copy of a NSManagedObject

I need to temporarily store the content of a NSManagedObject into a dictionary. Because core data has its own memory management procedures, I don't want to keep any strong pointers to the NSManagedObject's fields, only the values are of interest at this point (values are passed between view controllers, the MOCs are different). I can't create weak pointers either because I want to control when the memory reclaim is done.
I tried a few things, all failed or did not fit the purpose.
a duplicate [[myNSMO alloc] initWithEntity:[NSEntityDescription entityForName:entity inManagedObjectContext:myNSMO.managedObjectContext] insertIntoManagedObjectContext:nil];
It's working, but does not fit into my app design (without getting into details)
generate a NSDictionary from the NSManagedObject, using [myNSMO dictionaryWithValuesForKeys:<#(NSArray *)#>]. That's not ok because it returns a dictionary with the addresses of the NSManagedObject fields.
create a NSDictionary populating each key-value using a copyWithZone, like this
[myDictionary setObject:[myNSMO.field copyWithZone:nil] forKey:#"Key"];
Doesn't work either, I still get the field address...
Manually enter each field with
[myDictionary setObject:[NSString stringWithFormat:#"%#",myNSMO.field ] forKey:#"Key"];
It's fine this time, I do get new memory allocation. But that's highly time consuming to code this manually...
Any chance that someone found clever way to do that? the reason option 1) did not work is because I use the dictionary as a queue. I first store a copy of the object, then pop the entry out when required. A copy of that particular dictionary entry is then returned to the asking method. The problem is that I can't create a copy of an NSManagedObject that was created using [[...] insertIntoManagedObjectContext:nil];
Any solutions?
It's safe to keep strong pointers to the fields of a managed object in most senses — relationships are special but the actual Foundation objects of dates, strings and numbers are ordinary objects that'll stay in memory if you have a strong reference.
That being said, to create a dictionary copy containing all the properties of an entity you could do something like:
NSArray *properties = [[object entity] properties];
NSMutableDictionary *dictionaryRepresentation = [NSMutableDictionary dictionary];
for(NSAttributeDescription *attribute in properties)
{
// we want only actual attributes, not relationships
// or fetched properties
if([attribute isKindOfClass:[NSAttributeDescription class]])
{
[dictionaryRepresentation
setObject:[object valueForKey:attribute.name]
forKey:attribute.name];
}
}
So you're using the fact that managed objects expose a description of their entities which includes a list of properties, whittling those properties down to just the attributes, then using key-value coding to fetch the current value of each property and finally inserting it into the dictionary.
If for some reason you did want copies of the properties — though, as I say, there's absolutely no reason to do so — you'd copy (and autorelease if you're not using ARC) each property when inserting it into the dictionary.

Add objects to dictionary created by JSONKit?

In my project I have to load a number of json files. I parse them with JSONKit and after every single parsing with
NSMutableDictionary *json = [myJSON objectFromJSONString];
I add them to an array like:
[self.themeArray addObject:json];
This works fine so far. Now I need to pass the dictionaries arround between views. Works so far as well, but I need to add few more objects to the dictionary object-> json. Even it I declared json as NSMutableDictionary it does not allow me to add objects as it seems the JSONKit parser creates non-mutable dictionaries.
I was thinking about creating an object which contains the json dictionary and my additional data side by side so I wouldn´t have to change the json dictionary. I could even change it to NSDictionary because there is no need to change it. But that seems somehow not-elegant to me.
Do you have any idea how I can solve this issue without changing the JSONKit lib?
Thanks in advance!
EDIT
i just tried after changing my code to
NSMutableDictionary *json = [[myJSON objectFromJSONString] mutableCopy];
something like this
[[self.theme objectForKey:#"theme"] setObject:sender forKey:#"sender"];
[[self.theme objectForKey:#"theme"] setValue:sender forKey:#"sender"];
Xcode throws an exception:
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '* -[JKDictionary setObject:forKey:]: mutating method sent to immutable object'
I assume that´s due to the fact there are still nested dictionaries in the superior dictionary. Then i would have to interate through my json object to copy all dictionaries to mutable dictionaries, right?
Perhaps it's better to switch to NSJSONSerialization as suggested by Guillaume.
EDIT
I just tried something like this
[self.theme setValue:sender forKey:#"sender"];
And it works now! It was as i assumend. Only the json object was copied to a mutable object. Probably obvious to you, it was not to me.
Thank you all for your help!
EDIT
Finally i changed my code again after i could not manage to change all objects deep inside my dictionary data to mutable objects. I threw out JSONKit and use now NSJSONDeserialization as recommendet here with the option NSJSONReadingMutableContainers. My code looks now like this and all containers (arrays and dictionaries) are mutable deep inside too. That makes me happy! ;-)
NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:myJSON options:NSJSONReadingMutableContainers error:&jsonParsingError];
You can always create mutable versions of objects from their non-mutable counterparts by copying them.
NSMutableDictionary* json = [[myJSON objectFromJSONString] mutableCopy];
It is not optimal, but copying smaller dictionaries does is usually not noticable from a performance point of view.
Even it I declared json as NSMutableDictionary it does not allow me to add objects as it seems the JSONKit parser creates non-mutable dictionaries.
What type the variable is declared at means nothing. You could have declared json as NSNumber and that wouldn't make it an NSNumber.
You need to make a mutable copy of the dictionary (with mutableCopy) to get an NSMutableDictionary.
I have three ideas for your.
Create real data model objects and store them in your array. Use the JSON dictionary to init your object.
Store NSMutableDictionary objects in your array. Pass the JSON dictionary to +[NSMutableDictionary dictionaryWithDictionary:] to init the NSMutableDictionary. Others have suggested calling -[NSDictionary mutableCopy] on the JSON dictionary to do the same thing.
Create a category based on NSDictionary that stores the additional data.
NOTES:
Generally creating classes to represent your data is considered the best option, but it is also the most amount of up front work. Basically you are trading more up front work against more maintenance work as you try to keep up maintaining the dictionaries.
Storing mutable dictionary is exactly what you seem to be asking for, but it may be lots of works to find all the places where JSON dictionaries are added to the array and replacing them with the new call.
Creating a category for NSDictionary means you shouldn't need to change any of your current code, but it requires maintainers to understand how you have enhanced NSDictionary. In addition, it will help separate your changes from the original parsed JSON. You can use associated objects to store the data.

Resources