How to get object for key from NSDictionary? - iphone

In dictionary named dict the key value pair is:
"alba\U2019s window" = "A model in westindies.";
And to send objectForKey: I am getting the string like "alba's window". When I send like following:
[dict objectForKey:alba's window];
I am not getting anything, it is showing null.

For starters, you might want to make that an actual string, like so:
[dict objectForKey:#"alba's window"];
Note also that \U2019 is not '; but ’, which is a different character entirely.
And more generally, sticking to [a-zA-Z0-9]+ for dictionary keys is probably a good idea, unless you are inserting and retrieving programmatically using the exact same string as a key.

Since iOS6 onwards, a convenient method for setting and accessing the object for a key from an NSDictionary is:
//Setting the object in NSMutableDictionary
dictionary[#"someKey"] = #"someString";
//Accessing the object
NSString *str = dictionary[#"someKey"];

Make sure your dict isn't null; sending a message to a null object will silently fail and return null.
Another way to check your key/values is to simply NSLog(#"%#",dict); and this will show you the contents of the dictionary. Note that this output only shows quotes around values when the value contains a space.
Also, make sure you're using the same pairs of strings as the key - it looks like you're using "alba\U2019s window" in addition to "alba's window".

Related

What's wrong with my UserDefault code for global Array?

Trying to store an array through UserDefault, but Xcode gives me an error. The error message is Thread 1: Signal SIGABRT, and the console says "NSInvalidArgumentException, reason: Attempt to insert non-property list object". I have previously stored data in the array using this code:
let tempRecipe = GlobalFavorites(recipeImageObject: "", recipeTextObject: "", recipeHeaderObject: "", favoriteRecipeArray: [globalFavoriteRecipes])
tempRecipe.recipeHeaderObject = self.recipeClassArray[self.currentView].recipeHeaderObject
tempRecipe.recipeTextObject = self.recipeClassArray[self.currentView].recipeTextObject
tempRecipe.recipeImageObject = self.recipeClassArray[self.currentView].recipeImageObject
globalFavoriteRecipes.favoriteRecipeArray.append(tempRecipe)
And that works fine. Here's the code for storing with UserDefault that gives me the error:
UserDefaults.standard.setValue(globalFavoriteRecipes.favoriteRecipeArray, forKey: "savedFavoriteArray")
It's a global array and I want to store the whole array. I guess it has to do with how I write the array in UserDefault, because to me it seems that I'm trying to store something that's not there. Or what am I missing?
If globalFavoriteRecipes.favoriteRecipeArray is an array of custom objects, you need to make sure you are archiving and unarchiving them properly. Refer to this StackOverflow post.
That post also touches on some other options, as NSUserDefaults isn't the best place to store an array that can potentially grow pretty large, which it sounds like it could in this case based on the variable name.
Use set not setValue:
UserDefaults.standard.set(globalFavoriteRecipes.favoriteRecipeArray, forKey: "savedFavoriteArray")
But, I agree with #Jake, this should only be used to store small amounts of data, not a large array. Happy coding!

Adding multiple values as a single entry into an NSMutableArray and retrieving it

I retrieve 6 values(say name, age, sex, address, id, tag) from a web service. All are string variables. I concatenate these strings and add it to an NSMutableArray. I pass this array to another class, where I need each of these strings separately. That is I need to be able to retrieve these values from the array separately. How can I do this.
Do I need to add tags like "Name", "Age" etc along with the values to make the retrieval easier. Whats the appropriate way to do it.
Edit: i concatenate it into a single string. How should I be adding my values to the collection, so that I can retrieve the elements easily.
IMO, the most appropriate way of doing what you are trying to do is using an NSMutableDictionary, that allows you to access individual elements based on their key.
Example:
loadedBuffers = [[NSMutableDictionary alloc] initWithCapacity:CD_BUFFERS_START];
[loadedBuffers setObject:bufferId forKey:filePath];
...
[loadedBuffers objectForKey:filePath]
You do no strictly need using a dictionary, but it will make your life so much easier.
In your case (if I understand it correctly), I would do:
NSMutableArray* result = [NSArray arrayWithCapacity:kNUM_OF_ROWS];
NSString *name, *age, *sex....;
<for each set of strings from the web service>
<retrieve strings>
NSMutableDictionary dict = [NSMutableDictionary dictionaryWithCapacity:kNUM_OF_FIELDS];
[dict setObject:name forKey:#"name"];
...
[dict setObject:address forKey:#"address"];
[result addObject:dict];
<end_for>
return result;
By doing like this, you will be able to access sequentially each set of strings; then access each string individually.
In short, instead of encoding your set of strings by concatenating them into another string, you would expand them in a dictionary to make retrieval easier.
I would agree that the best practise here would be to use a dictionary or custom object. That way each string gets stored with its companions (e.g. you have one person's data all together) and you don't have to deal with the messy method you already have implemented. It sounds like you might want to save data, so here's a snippet to help you. If that's not what you're after, let me know and I'll modify my response to help!
Say you have a custom object class Person, where you create and manage data objects to save to disk via the app delegate. You'd do something like:
Person *newPerson = [[Person alloc] init];
[newPerson setName:#"John"];
[newPerson setAge:#"25"];
[newPerson setSex:#"M"];
[yourAppDelegate.newPersonArray insertObject:newPerson atIndex:[mainDelegate.newPersonArray count]];

Object is deallocated - why? where?

Ok, I spent the last 8 hours fighting with it - it just seems beyond me. Here's my complete (relevant) code:
- (void)updateUserDefaults
{
NSMutableDictionary *viewControllerDetails = [[NSMutableDictionary alloc] initWithCapacity:4];
[viewControllerDetails setObject:[NSNumber numberWithInt:OOVenueClassControllerType] forKey:#"classType"];
[viewControllerDetails setObject:self.searchTerm forKey:#"searchTerm"];
[viewControllerDetails setObject:self.searchLocation forKey:#"searchLocation"];
//----- the next two lines cause the problem
NSString *res = [[NSString stringWithFormat:#"%#",[searchResults xmlString]] retain];
[viewControllerDetails setObject:res forKey:#"searchresults"];
//-----
NSMutableArray *viewControllersList = [NSMutableArray array] ;
[viewControllersList addObject:viewControllerDetails];
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
//the following line causes the error
[defs setObject:viewControllersList forKey:kViewControllersKey];
[defs synchronize];
[res release];
}
Note the block with the next two lines cause the problem. At first I didn't create another string, but added it later while trying to solve the problem.
If I comment out those two lines, everything works fine. If I put them back in, I get
- [CFString class]: message sent to deallocated instance 0xa1a9000
Is something is wrong with the string that I'm trying to put into the userdefaults? That string is rather large (about 200,000 characters), but I had stored even longer strings in user defaults in the past.
It's also worth noting that if I uninstall the app, then everything works fine. But on subsequent runs the problem exhibits itself.
So, how and why and where is the string getting deallocated? I have explicitly added retain - but that still doesn't help. What am I missing?
Edit: just realised I forgot to say that the error is thrown on line
[defs setObject:viewControllersList forKey:kViewControllersKey];
Also, for general information, method - (NSString *)xmlString on searchResults does exactly what the name means: creates an XML string with the information from that object.
Edit 2: I tried doing something else with that string - convert it to NSData, compress using zlib - but regardless of data type, that particular object gets deallocated. Does it have to do something with the size of the string?
NSString *res = [[NSString stringWithFormat:#"%#",[searchResults xmlString]] retain];
Is auto released. You don't need to release it at the end of your method. You are over-releasing it.
Further, you don't need to retain the [searchResults xmlString]. The stringWithFormat method already does it for you.
Good Luck!
Ok, not sure what exactly the problem was, but it was somewhere in the searchResults and/or xmlString method. searchResults object is originally created from XML received from the server (XML is parsed into the object structure). When xmlString was called, for some reason the string I was getting back was different from the original XML (I'm not talking about formatting, of course) - of 200,000 char-long string, within the first 500 chars or so there were some differences. I haven't been able to figure out why. So, instead of recreating the xml from object structure, I instead stored the original XML in a field in that object and, when xmlString was called, simply returned the original string. Now everything worked fine.
Thank you all for your support through this painful process.

Objective C - Problem with objectForKey

Okay, I'm trying to write a high score function for my app.
My problem is that when no high score has been saved yet, my program crashes.
If I save it with:
[[NSUserDefaults standardUserDefaults] setObject:#"[given string]" forKey:#"firstName"];
first, it works fine. However, if I start up the program for the first time and try to view the high scores with the following code:
first = [[NSString alloc] initWithString:[[NSUserDefaults standardUserDefaults] objectForKey:#"firstName"]];
bad things happen.
Basically, is there away to see if nothing yet exist under firstName? Is there a way to initialize without erasing any name that might already be present?
Thanks.
The NSString documentation for initWithString: says
Parameters
aString
The string from which to copy characters. This value must not be nil.
The documentation for objectForKey: says
Return Value
The object associated with the
specified key, or nil if the key was
not found.
The problem seems to be that there is a nil returned when you try to retrieve firstName that doesn't exist yet and try to create a NSString with it as input.
The NSUserDefaults instance method registerDefaults: is meant for exactly this purpose: You can set default values for your preferences that will be overridden by any other value set for the same preference key. Just make sure to call it early enough that it will run before any code that needs to access your preferences.
You could load "first" like this:
first = [[[NSUserDefaults standardUserDefaults] objectForKey:#"firstName"] retain];
if (!first) {
// set default or do something else if there wasn't a value saved
first = #"N/A";
}

iPhone: NSMutableDictionary key

I was trying to use a CLLocation object as a key for a dictionary... but it doesnt seem to work. Works well if I use a string. Is this not possible?
[dict setObject:tmp forKey:tmpLocation]; // objectForKey always returns null on same location
I ended up passing a string lat/long combo which served my purpose.