NSInteger not equal to long? - objective-c

I am trying to parse some json data in Cocoa and I am having troubles with the NSInteger data type. The json string has some long values that I assign to NSInteger properties. Unfortunately the assigned NSInteger value differs completely from the long value. Why is that so? NSInteger is defined as typedef long NSInteger. I could have assigned the long value to a long property but I just would like to know why I can't assign it to an NSInteger.
-(void)parseData:(NSData*)data
{
NSError*err=nil;
NSDictionary*jsonData=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&err];
_userID=(NSInteger)[jsonData valueForKeyWithoutNSNull:#"id"];
}
The _userID is a NSInteger.
The value retrieved from the dictionary is a long.

You can't simply cast an NSNumber (or even NSString) to an NSInteger. Dictionaries and other collection classes can't store primitive types like NSInteger.
Assuming your dictionary contains numbers and not strings then you need:
NSNumber *number = [jsonData valueForKeyWithoutNSNull:#"id"];
_userID = [number integerValue];

Related

arc4random in NSNumber giving negative values?

I'm creating a random number and storing it in a NSNumber object like this:
NSNumber *index = [NSNumber numberWithUnsignedInt:arc4random_uniform(2^32-1)];
I also tried:
NSNumber *index = [NSNumber numberWithUnsignedInt:arc4random_uniform(4294967295)];
NSNumber *index = #(arc4random_uniform(4294967295));
At some point I'm also assigning the number 1 like this:
NSNumber *index = #(1);
This should give me only positive numbers.
Later on, I print out these numbers like this:
NSString *string = [NSString stringWithFormat:#"%#", index];
This gives me some negative values for the random numbers and 1 is being printed as 1. So I though maybe if I do:
NSString *string = [NSString stringWithFormat:#"%u", index.unsignedIntValue];
I'll get only positive numbers - which I do - but now 1 is suddenly being printed as some large positive number, also.
What's going on here? How can I correctly store a u_int32 (which arc4random returns) in a NSNmber and make sure that they are only positive?
Use
NSNumber *index = [NSNumber numberWithUnsignedInt:arc4random_uniform(exp2(32)-1)];
I never get any negative numbers. arc4random_uniform(x) always returns a number between 0 and x, and the stringvalue of the NSNumber generated from it is correct.
EDIT: replaced exp2(31) with exp2(32)
You said in a comment that the index is stored in a Core Data entity as an "Integer 32" attribute, and I assume that is where the problem comes from.
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.
For an "Integer 32" attribute, Core Data uses a (signed) integer for the attribute, and when you set a value, it is just cast or truncated to int. Example:
e.index = [NSNumber numberWithUnsignedInt:0xFFFFFFF0U];
// This would give the same result:
// e.index = [NSNumber numberWithLongLong:0x1234FFFFFFF0LL];
NSNumber *val = e.index;
NSLog(#"value=%#, type=%s", val, [val objCType]);
// Output: value=-16, type=i
The output type=i shows that the value contains an int.
If you need unsigned integers in the range 0 .. 2^32-1, then you can either (as you already did) use unsignedIntValue:
unsigned x = [val unsignedIntValue];
or store the attribute as "Integer 64".
Remarks:
I am fairly sure that this is not a problem of arc4random_uniform.
In your first code example arc4random_uniform(2^32-1), you should note that ^ is exclusive-or, not exponentiation.

discussing functionality of id's stringValue function?

I got a NSMutableArray object with int values
and I can get a certain value via :
int *v0=[[[arrayObj objectAtIndex:0] intValue];
there is no problem.
But
I got a NSMutableArray object with NSString values
and I cannot get a certain value via :
NSString *v0=[[[arrayObj objectAtIndex:0] stringValue];
//raises error
I want to learn and understand exactly what stringValue for... and why this error occurs ?
NSString *v0=[arrayObj objectAtIndex:0];
works as expected.I asusme its some kind of pointer with null terminated so it can leech value.
Im not sure this line is also unicode/encoded string safe code.
in conclusion:
want to know the purpose of stringValue with some lines o code snippets
I got a NSMutableArray object with int values
That's not possible, Cocoa arrays always contain objects. You probably have an array of NSNumber objects that wrap the integers, like:
NSArray *arrayOfNumbers = #[#1, #2, #3];
NSNumber objects have an intValue method, so this works:
int value = [arrayOfNumbers[0] intValue];
On the other hand when you have an array of strings ...
NSArray *arrayOfStrings = #[#"1", #"2", #"3"];
... you want to access individual elements directly, without converting the string object to something else:
NSString *element = arrayOfStrings[0];
NSString objects do not understand the stringValue method:
[arrayOfStrings[0] stringValue]; // crash: does not recognize selector
Back at the beginning, our NSNumber objects from the first array do understand stringValue. You can use it to convert the number to a string:
NSString *intString = [arrayOfNumbers[0] stringValue];
To make the confusion perfect, NSString also understand the intValue message:
int value = [arrayOfStrings[0] intValue];
Here intValue means to try to convert the string to a plain C int value.
The error you will be getting (but failing to post with your question) will be Unknown selector sent to instance and this is because NSString doesn't have a stringValue method.
The approach you suggest is correct:
NSString *v0 = [arrayObj objectAtIndex:0];
EDIT (prompted by #Answerbot's answer):
The reason you are confused is that [NSString intValue] is used to convert the string value to an integer, as long as the string represents an integer (i.e. #"123"). However you don't need this for string as the object is already a string. It's therefore not provided.

Acquiring NSInteger Value

I am trying to grab the value of an NSInteger and set a UILabel value to it. The code is as follows:
counter_.text = [NSString stringWithFormat:#"%#", [counterAmount intValue]];
where counter_ is my label and counterAmount is my NSInteger. I keep getting a "Receiver type 'NSInteger *' (aka 'int *') is not 'id' or interface pointer, consider casting it to id.
I'm not quite sure how to understand this. I appreciate of your help.
intValue is an NSNumber method that returns a C primitive value. %# prints objects. In order to output a C primitive value you have to supply the type in the formatting string. %d is the type for signed integer.
As per the question, and as pointed out to me quite correctly by borrden below, the type being dealt with here is NSInteger rather than NSNumber. In iOS NSInteger is a typedef of int, so you're dealing directly with a primitive type. The NS prefix does not mean that its an object.
Since counterAmount is a NSInteger, you can not use intValue on it (since it isn't an object, you can't send any messages to it, actually).
Instead, use:
counter_.text = [NSString stringWithFormat:#"%d", counterAmount];
Now that being said, if you are displaying this value to a user you should really be using a number formatter so that it is formatted the way that they have set it up to display in the settings app:
NSNumber *number = [NSNumber numberWithInt:counterAmount];
NSNumberFormatter *formatter = [NSNumberFormatter new];
formatter.numberStyle = NSNumberFormatterDecimalStyle; // Or whichever style is appropriate for your number.
counter_.text = [formatter stringFromNumber:number];

KVC - about data type

this is the code:
NSNumber *taskId = [[self.taskList objectAtIndex:indexPath.row] valueForKey:#"identity"];
NSInteger *intTaskId = [[self.taskList objectAtIndex:indexPath.row] valueForKey:#"identity"];
self.taskList is an NSArray which filled with core data fetch request in ViewController's viewDidLoad method.
the taskId is: 1
the intTaskId is: 269303816
In actually, the value stored in core data is: 1
below is my questions:
1, I am confused why the NSInteger incorrect?
2, Should I have to replace NSInteger with NSNumber to avoid any other problems?
NSNumber is an object, whereas NSInteger is simply a typedef for a primitive (non-object) type (like int). NSInteger is not a subclass of NSNumber. Core Data returns numbers as instances of NSNumber. You're getting the weird NSInteger value because it's pointing to an object of type NSNumber but attempting to print it as if it were just an integer.
You'll need to replace NSInteger with NSNumber to avoid any problems. You could also use the intValue method on NSNumber to get back an NSInteger:
NSNumber *objTaskId = [[self.taskList objectAtIndex:indexPath.row] valueForKey:#"identity"];
NSInteger *intTaskId = [objTaskId intValue];
You'll need to do this if you want to do comparisons (greater than, equal too, smaller than) or arithmetic (you can't add an NSNumber to another NSNumber or an NSNumber to a primitive type like an int or float).

How can I typecast an NSArray element to double?

An other way please.
I have an array whole elements are doubles. How can I get the content out as double?e.g.
coordinate.latitude = [coords objectAtIndex:0];// value inside it is 61.2180556;
You can't store primitive doubles in NSArray. You have either stored them as NSNumber or NSString. In either case use:
coordinate.latitude = [[coords objectAtIndex:0] doubleValue];