NSUserDefaults: How to display user defaults when valueforKey is an ENUM - iphone

In iPhone app, How to display values in console for user defaults when valueforKey is an ENUM?
Currently with the below code if I try to display in console then it crashes with no crash log in console.
NSLog(#"%#",[[NSUserDefaults standardUserDefaults] valueForKey:Enum]);

To fetch:
int someValue = [[NSUserDefaults standardDefaults] integerForKey:#"your key here"];
to save:
[[NSUserDefaults standardDefaults] setInteger:10 forKey:#"your key here"];
EDIT: got it, you crash because in NSLog you are using the wrong format:
NSLog(#"%i",[[NSUserDefaults standardUserDefaults] valueForKey:Enum]);
try %i (to print integers) instead of %# (used to print valid objective-c objects)

Since an enum is really just a fancy way of displaying an int, all you have to do is create an enum variable and set it to the value you get from NSUserDefaults.
You may end up with something like:
PirateEnumType pirateType = [[NSUserDefaults standardUserDefaults] integerForKey:#"PirateType"];
EDIT:
I'm sorry. I guess I didn't fully understand your question. Since an enum is really an int, you will need to use "%i" to display it instead of "%#".
In order to show it properly as a human readable string, you would need to have some sort of enumToString function, perhaps like:
-(NSString*)enumToString:(PirateEnumType)enumValue {
NSString* returnValue = #"";
switch (enumValue) {
case Captain:
returnValue = #"Captain";
break;
case Swashbuckler:
returnValue = #"Swashbuckler";
break;
case PegLeg:
returnValue = #"PegLeg";
break;
}
return returnValue;
}

NSUserDefaults objects are really just NSDictionary objects. NSDictionary only takes an object as a key. Enum is just an integer, so you can't use it as a key directly. One way to do it, though is to use NSNumber, which can represent an integer as an object like the following:
[[NSUserDefaults standardDefults] valueForKey:[NSNumber numberWithInt:yourEnum];
To set the value you would do:
[[NSUserDefaults standardDefaults] setValue:yourValue forKey:[NSNumber numberWithInt:yourEnum]];

There are two issues here. Firstly, an enum cannot be a key in valueForKey. valueForKey can only accept strings, not string values of enums. Therefore, you need to implement a method to get a string value from an enum. In my example below that method is stringFromEnum. Here is one example of how to get a string from an enum.
Sometimes I've seen NSLog choke when I ask it to directly include the return value of a message sent to an object. I've always found that assign the result of that message to an instance variable first and then passing the value of that instance variable into NSLog instead solved the problem, but admit I haven't figured out what the relevant factors are that determine when it does or does not work the original way. Interested in any comments.
NSString *myKey = [self stringFromEnum:Enum];
NSString *myStringToLog = [[NSUserDefaults standardUserDefaults] valueForKey:myKey];
NSLog(#"%#", myStringToLog);
Another reading of your question could be that your instance variable Enum is actually a string, which is the Key for an enum that will be returned from NSUserDefaults. However, that can't be the case, as NSUserDefaults will not store an enum. See the documentation for object types that are supported. If that's what you're trying to do, your problem may be partly in other code where you're trying to store the enum in the first place?

Related

Unable to save array to NSUserDefaults

This is my first attempt at using NSUserDefaults. I've read every question & answer posted in stackoverflow regarding this subject, but still can't get it to work. I must be missing something basic. The array (allContacts) merely contains a few names and phone numbers. Unless I'm misunderstanding what's happening, both fields are NSStrings. Or are they just pointers to strings? If that's the case, how would I convert them to actual NSStrings? Here's my code to save the array:
- (BOOL)saveChanges
{
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:allContacts];
[[NSUserDefaults standardUserDefaults] setObject:mutableArray forKey:#"allContacts"];
[[NSUserDefaults standardUserDefaults] synchronize];
return 1;
}
Here my log:
2013-08-07 15:48:17.568 ImOK[5515:907] *** -[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value '(
"Brad Pitt, 1-917-297-1234",
"Marilyn Monroe, 9179291234"
)' of class '__NSArrayM'. Note that dictionaries and arrays in property lists must also contain only property values.
Thanks in advance for any help.
This code "for(id contact in allContacts) { NSLog(#"%#", [contact class]); }" gives me:
2013-08-07 17:11:30.845 ImOK[5569:907] Contacts
2013-08-07 17:11:30.854 ImOK[5569:907] Contacts
so I guess they are not valid property values.
Incidentally, there was no intelligent reason for me to attempt to save the array as an NSMutableArray. I was just experimenting.
From the Apple documentation: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html#//apple_ref/doc/c_ref/NSUserDefaults
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.
If allContacts is an NSArray, just use that in your setObject:forKey: call. If it's an NSMutableArray, use:
[allContacts copy]
to get a non-mutable copy of the array.

NSLog pointer syntax

I'm a little bit confused about the syntax of NSLog. For example,
NSString *nameString = #"Name";
NSLog(#"nameString is: %#", nameString);
If my understanding is correct (which it very well may not be), then nameString is defined to be a pointer to a String. I thought then that this would print the memory address that nameString holds, not the value of that address. So, if that is true, then in the NSLog statement, to get the value of the pointer, shouldn't we need to use the asterisk notation to access what nameString points to like this:
NSLog(#"nameString is: %#", *nameString);
?
It has been a little while since programming in C, but since Objective-C is a superset of C I thought they would behave similarly.
An explanation would be greatly appreciated! Thanks!
The command %# is like "shortcut" that calls the method -description on the receiver. For an NSString it simply display the string itself, since is inherited from NSObject you can override it, very usefull if you create for own class. In that case the default behaviur is print the value of the pointer. If you want to print the address of the pointer in the string just replace with :
NSLog(#"nameString is: %p", nameString)
I think that you use an asterisk only to declare a pointer. Then, you only use the name you decided. For example:
NSString *foo = [[NSString alloc] initWithString:#"Hello"];
NSLog(#"%#", foo);
Correct me if I am wrong :)
It's an object and NSLog is a function that uses its format specifiers to determine what to do with the argument. In this case the specifier is %# which tells NSLog to call a method on an object.
Normally this will call the method "description" which returns an NSString but it probably does respondsToMethod first and falls through to some other string methods.

NSDictionary with integer as number

I have a NSDictionary with the following layout:
{
1:{
... some data ...
}
...
}
I have a NSNumber object with a integer value of 1, but when I do
[my_dict objectForKey:my_number] it returns null.
If I try and convert NSNumber to a integer via [my dict objectForKey:[my_number intValue]] I get a warning and the program crashes when it reaches that part of the code.
Any help would be greatly appreciated.
Keys in a NSDictionary or NSMutableDictionary must be objects, like NSNumber. They cannot be primitive data types, like int.
http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/Reference/Reference.html
Looks like you're trying to use an integer as the key in your NSDictionary. This would be correct with an NSArray, with an NSDictionary actually needs a proper object as a key.
You might have more success in this particular case feeding that data into an NSArray, and accessing it with:
id *someData = [my_array objectAtIndex:1];

What kind of data is in an "enum" type constant? How to add it to an NSArray?

What kind of information is stored behind such an enum type thing? Example:
typedef enum {
UIViewAnimationCurveEaseInOut,
UIViewAnimationCurveEaseIn,
UIViewAnimationCurveEaseOut,
UIViewAnimationCurveLinear
} UIViewAnimationCurve;
I am not sure if I can safely add such an enum constant to an array. Any idea?
Enums in Objective-C are exactly the same as those in C. Each item in your enum is automatically given an integer value, by default starting with zero.
For the example you provided: UIViewAnimationCurveEaseInOut would be 0; UIViewAnimationCurveEaseIn would be 1, and so on.
You can specify the value for the enum if required:
typedef enum {
UIViewAnimationCurveEaseInOut,
UIViewAnimationCurveEaseIn = 0,
UIViewAnimationCurveEaseOut,
UIViewAnimationCurveLinear
} UIViewAnimationCurve;
This result of this would be: UIViewAnimationCurveEaseInOut is 0; UIViewAnimationCurveEaseIn is 0; UIViewAnimationCurveEaseOut is 1; and so on. However, for basic purposes you shouldn't need to do anything like that; it just gives you some useful info to toy with.
It should be noted based on the above, that an enum can't assume to be a unique value; different enum identifiers can be equal in value to each other.
Adding an enum item to a NSArray is as simple as adding an integer. The only difference would be that you use the enum identifer instead.
[myArray addObject:[NSNumber numberWithInt:UIViewAnimationCurveEaseInOut]];
You can check this out for yourself by simply outputting each enum to the console and checking the value it provides you with. This gives you the opportunity to investigate the details of how it operates. But for the most part you won't really need to know on a day to day basis.
Enums are typically int values. You can store them in an array by wrapping them in an NSNumber:
[myMutableArray addObject:[NSNumber numberWithInt:myAnimationCurve]];
... then get them back out like this:
UIViewAnimationCurve myAnimationCurve = [[myMutableArray lastObject] intValue];
Enums in Objective-C are the same as enums in vanilla C. It's just an int. If you're using an NSArray, then it expects a pointer and you'll get a warning if you try to add an int to it:
NSMutableArray *myArray = [[NSMutableArray alloc] init];
[myArray addObject:UIViewAnimationCurveEaseInOut];
// Last line results in:
// warning: passing argument 1 of 'addObject:' makes
// pointer from integer without a cast
If you're storing a large collection of 32-bit integers, consider using the appropriate CF collection type rather than the NS collection type. These allow you to pass in custom retain methods, which gets rid of the need to box every integer added to the collection.
For example, let's say you want a straight array of 32-bit ints. Use:
CFMutableArrayRef arrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
The last parameter tells the array to not retain/release the "addresses" you pass in to it. So when you do something like this:
CFArrayAppendValue(arrayRef, 1);
What the array thinks is that you're passing in a pointer to an object living at the memory address 0x1. But since you told it to not call retain/release on that pointer, it gets treated as a standard int by the collection.
FWIW, for educational value, standard NSMutableArrays have equivalent CF types. Through toll-free bridging you can use the CF collection as a standard Foundation collection:
CFMutableArrayRef arrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, kCFTypeArrayCallbacks);
NSMutableArray *array = (NSMutableArray *)arrayRef;
[array addObject:#"hi there!"];
NSLog(#"%#", [array objectAtIndex:0]); // prints "hi there!"
You can apply the same tricks to dictionaries (with CFDictionary/CFMutableDictionary), sets (CFSet/CFMutableSet), etc.

NSNumber, Setting and Retrieving

I'm messing around with NSNumber for an iPhone app, and seeing what I can do with it. For most of my variables, I simple store them as "int" or "float" or whatnot. However, when I have to pass an object (Such as in a Dictionary) then I need them as an Object. I use NSNUmber. This is how I initialize the object.
NSNumber *testNum = [NSNumber numberWithInt:varMoney];
Where "varMoney" is an int I have declared earlier in the program. However, I have absolutely no idea how to get that number back...
for example:
varMoney2 = [NSNumber retrieve the variable...];
How do I get the value back from the object and set it to a regular "int" again?
Thanks!
(Out of curiosity, is there a way to store "int" directly in an Objective-C dictionary without putting it in an NSNumber first?)
You want -intValue, or one of its friends (-floatValue, -doubleValue, etc.). From the docs:
intValue Returns the receiver’s value
as an int.
- (int)intValue
Return Value The receiver’s value as
an int, converting it as necessary.
The code would be:
int varMoney2 = [testNum intValue];
NSNumber *testNum = [NSNumber numberWithInt:varMoney];
/* Then later... */
int newVarMoney = [testNum intValue];