NSNumber, Setting and Retrieving - iphone

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];

Related

Core Data Boolean property NSNumber doesn't remember it's boolean

I have a model with a property that looks like this:
When I set its value, for example:
model.isResolved = #YES;
The NSNumber that's kept inside the model "forgets" that it's a boolean:
NSLog(#"%#", strcmp([self.isResolved objCType], #encode(BOOL)) == 0 ? #"equal" : #"different");
Prints "different". What is up with that?
What is up with that?
From the documentation:
Note that number objects do not necessarily preserve the type they are created with.
That's another inconsistency-for-optimization in Cocoa.
Core Data dynamically generates getter and setter methods for all attributes (and relationships) of managed object classes. These accessor methods are different from the "usual" #synthesized accessor methods which are backed up by an instance variable.
In particular, if you set an attribute and then retrieve the attributes value again, you can get an object that is different from the "original" object. The following test shows this, foo1 is an instance of a Core Data entity with the Boolean attribute "show":
NSNumber *yes = #YES;
NSLog(#"yes = %p, type = %s", yes, [yes objCType]);
foo1.show = yes;
NSNumber *val = foo1.show;
NSLog(#"val = %p, type = %s", val, [val objCType]);
Output:
yes = 0x16e595c, type = c
val = 0x744c150, type = i
So even if you set the attribute to a c = char encoded number, the getter method returns a i = int encoded number.
This test was done in the iOS Simulator. Interestingly the same test, running on OS X 64-bit, returns a c = char encoded number.
Therefore, the actual encoding of Boolean and other scalar attributes in Core Data objects should probably be treated as an implementation detail of Core Data.
If you need to check the Core Data type as defined in the model, you can use the objects entity description instead of objCType:
NSEntityDescription *entity = [foo1 entity];
NSAttributeDescription *attr = [[entity attributesByName] objectForKey:#"show"];
NSAttributeType type = [attr attributeType];
if (type == NSBooleanAttributeType) {
NSLog(#"Its a Boolean!");
}
Its stored as a NSNumber - by the way #YES is creating a NSNumber like
[NSNumber numberWithBool:YES]
so to get the bool back out you do:
[isResolved boolValue]
(you can avoid this by ticking the Use Scalar Properties when you create your models)

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

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?

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];

ObjectiveC Parse Integer from String

I'm trying to extract a string (which contains an integer) from an array and then use it as an int in a function. I'm trying to convert it to a int using intValue.
Here's the code I've been trying.
NSArray *_returnedArguments = [serverOutput componentsSeparatedByString:#":"];
[_appDelegate loggedIn:usernameField.text:passwordField.text:(int)[[_returnedArguments objectAtIndex:2] intValue]];
I get this error:
passing argument 3 of 'loggedIn:::' makes pointer from integer
without a cast
What's wrong?
I really don't know what was so hard about this question, but I managed to do it this way:
[myStringContainingInt intValue];
It should be noted that you can also do:
myStringContainingInt.intValue;
You can just convert the string like that [str intValue] or [str integerValue]
integerValue
Returns the NSInteger value of the receiver’s text.
(NSInteger)integerValue
Return Value
The NSInteger value of the receiver’s text, assuming a decimal representation and skipping whitespace at the beginning of the string. Returns 0 if the receiver doesn’t begin with a valid decimal text representation of a number.
for more information refer here
NSArray *_returnedArguments = [serverOutput componentsSeparatedByString:#":"];
_returnedArguments is an array of NSStrings which the UITextField text property is expecting. No need to convert.
Syntax error:
[_appDelegate loggedIn:usernameField.text:passwordField.text:(int)[[_returnedArguments objectAtIndex:2] intValue]];
If your _appDelegate has a passwordField property, then you can set the text using the following
[[_appDelegate passwordField] setText:[_returnedArguments objectAtIndex:2]];
Basically, the third parameter in loggedIn should not be an integer, it should be an object of some kind, but we can't know for sure because you did not name the parameters in the method call. Provide the method signature so we can see for sure. Perhaps it takes an NSNumber or something.
Keep in mind that international users may be using a decimal separator other than . in which case values can get mixed up or just become nil when using intValue on a string.
For example, in the UK 1.23 is written 1,23, so the number 1.777 would be input by user as 1,777, which, as .intValue, will be 1777 not 1 (truncated).
I've made a macro that will convert input text to an NSNumber based on a locale argument which can be nil (if nil it uses device current locale).
#define stringToNumber(__string, __nullable_locale) (\
(^NSNumber *(void){\
NSLocale *__locale = __nullable_locale;\
if (!__locale) {\
__locale = [NSLocale currentLocale];\
}\
NSString *__string_copy = [__string stringByReplacingOccurrencesOfString:__locale.groupingSeparator withString:#""];\
__string_copy = [__string_copy stringByReplacingOccurrencesOfString:__locale.decimalSeparator withString:#"."];\
return #([__string_copy doubleValue]);\
})()\
)
If I understood you correctly, you need to convert your NSString to int? Try this peace of code:
NSString *stringWithNumberInside = [_returnedArguments objectAtIndex:2];
int number;
sscanf([stringWithNumberInside UTF8String], "%x", &flags);

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.