NSNumber limit? - objective-c

What is the highest int that NSNumber allows? I've seen the answer elsewhere on these forums hence why I'm deeply confused here.
int miles = 35000;
vehicle.mileage = [NSNumber numberWithInt:miles];
NSLog(#"int value = %d", miles);
NSLog(#"mileage = %#", vehicle.mileage);
The output is:
int value = 35000
mileage = -30536
I must be missing some terrible easy here, but can someone explain to me why this is not working correctly?
UPDATE:
After looking further, vehicle.mileage is getting set correctly to 35000 but when I display this via NSLog(#"%#", vehicle.mileage) it is outputting it incorrectly. I have yet to find the "magic" value when this stops working because as of now, it works for values up to ~30,000.

NSNumber is just a wrapper so it goes in overflow when the wrapped primitive type goes in overflow.
So if you use numberWithInt the maximum number allowed is INT_MAX (defined in limits.h), if you use a numberWithFloat the maximum number allowed is FLOAT_MAX, and so on.
So in this case you aren't going in overflow, I doubt that INT_MAX would be so low.

Overview
NSNumber is a subclass of NSValue that offers a value as any C scalar
(numeric) type. It defines a set of methods specifically for setting
and accessing the value as a signed or unsigned char, short int, int,
long int, long long int, float, or double or as a BOOL. (Note that
number objects do not necessarily preserve the type they are created
with.) It also defines a compare: method to determine the ordering of
two NSNumber objects.
So NSNumber is as big as what it wraps. For your unexpected result you can check comment bellow your qestion from #sjs.

+numberWithInt: interprets the value as signed int. Mileage would never be negative, so I suggest using [NSNumber numberWithUnsignedInt:]

The limit NSNumber integer can have is known as INT_MAXbut 35, 000 is nowhere close to that. The problem must be with vehicle object or the mileage property in the vehicle, either of them may be nil
So, go ahead and log with this conditional statement:
if (!vehicle) {
NSLog(#"Vehicle is nil");
}
else if (!vehicle.mileage) {
NSLog(#"Vehicle's mileage is nil");
}
Tell me your result

Related

Having issues when trying to convert string input to an integer value that can be set as an entities int32 attribute

I am still fairly new to Objective-C and iOS development
. I am able to make an app run fine with core data when all the attributes are strings only. My problem occurs when i have an entity (i made a test one to show as an example) which has an attribute that is set to be of type integer 16(though i have tried setting both integer 16 and integer 64 and get the exact same errors) but i cannot seem to understand how i am supposed to convert the string input from a user to a format which will be accepted as a value to be set. I keep getting the same error messages (implicit conversion of NSInteger to IDNullable is disallowed in ARC) & (incompatible integer to pointer conversion sending NSInteger(AKA "long" to parameter of type ID Nullable)
ex 1:
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *newEntity = [NSEntityDescription insertNewObjectForEntityForName:#"TestEntity" inManagedObjectContext:context];
int valueOne = [self.valueOneIn.text intValue];
[newEntity setValue:valueOne forKey:#"value1"]; //!!2 errors listed above
ex 2 (above ex edited):
NSInteger valueOne = [self.valueOneIn.text intValue];
[newEntity setValue:valueOne forKey:#"value1"]; //!!2 errors listed above
ex 3 (ex1, just edited):
NSInteger *valueOne = [self.valueOneIn.text intValue]; //!!error
[newEntity setValue:valueOne forKey:#"value1"]; //!!2 errors listed above
I have attached two photos showing simple examples of the errors that i am getting. I have spent the past couple days looking up videos, online courses and even reading some possible solutions on stack overflow, but none seem to remedy the situation (my examples above were made in my attempts to use the potential solutions i had found but, most cover using core data and string values or NSDate values). Any help or nudge in the right direction would really (i cannot stress this enough, i mean really) be appreciated[example of error when setting to NSIntegerexample of error with NSInteger
The setValue:forKey: method wants an object as the value. But int and NSInteger are primitive numeric types, not objects. That's why the first two examples don't work. The third one doesn't work because a pointer to NSInteger is still not an object.
Assuming that valueOneIn is a text field, you should do something like:
NSInteger valueOne = [self.valueOneIn.text integerValue];
[newEntity setValue:#(valueOne) forKey:#"value1"];
The #(valueOne) syntax tells the compiler to convert the NSInteger variable valueOne to an instance of NSNumber. That's a class designed to wrap numeric values when objects are required, so it's what you need for setValue:forKey:. Also, note that the code uses integerValue instead of intValue-- which is better because the compiler will use the correct integer size depending on the platform you're targeting.
It would be better to use custom NSManagedObject subclasses for your entities than to use NSManagedObject directly. One major advantage is that setValue:forKey: will accept any object as the value. Subclasses will tell the compiler what object types are acceptable, so that the compiler can verify that you're using the correct types.

Objective-C, what is the return value of getting a property from a nil pointer?

I always thoughts that sending a message to a nil pointer would normally return 0. So the same would apply to properties. But then this code snippet seems to contradict my assumptions
NSArray *testArray;
NSInteger i = 0;
NSLog(#"testArray.count-1=%ld", testArray.count-1);
NSLog(#"i<testArray.count-1=%d", i<testArray.count-1);
The output is
2013-05-22 11:10:24.009 LoopTest[45413:303] testArray.count-1=-1
2013-05-22 11:10:24.009 LoopTest[45413:303] i<testArray.count-1=1
While the first line makes sense, the second does not. What am I missing?
EDIT: thanks to #JoachimIsaksson and #Monolo for pointing (pun intended) me to the right direction. The problem is actually signed v. unsigned and the following code shows it:
NSArray *testArray;
NSInteger i = 0;
unsigned ucount = 0;
int count = 0;
NSLog(#"testArray.count-1=%ld", testArray.count-1);
NSLog(#"i<testArray.count-1=%d", i<testArray.count-1);
NSLog(#"i<ucount-1=%d", i<ucount-1);
NSLog(#"i<count-1=%d", i<count-1);
And the output is
2013-05-22 11:26:14.443 LoopTest[45496:303] testArray.count-1=-1
2013-05-22 11:26:14.444 LoopTest[45496:303] i<testArray.count-1=1
2013-05-22 11:26:14.444 LoopTest[45496:303] i<ucount-1=1
2013-05-22 11:26:14.445 LoopTest[45496:303] i<count-1=0
Return values from a nil receiver
When accessing properties or reading return values from a nil object, you will get their default value. This is usually 0 for any numeric return type. So getting the count of an array when it is nilled will yield 0. Other possible values from nil receivers are NO for BOOLs, and nil for object return types. Returned structures have all members initialized to zero.
Array counts are unsigned...
Now, you need to remember that an array count returns an NSUInteger. With this being unsigned, if you subtract from 0, you will underflow, and get a very large number.
Why does NSLog print -1 for the first statement then?
It's because you have used #"%ld", which specifies a long signed integer. As such, the value is interpreted as signed, and this results in -1. The type of the variable actually states it's an unsigned long, whose format specifier should be #"lu". When using this, it results in 18446744073709551615 for me (could vary for you, depending on platform).
How does this affect the second NSLog statement?
Taking into account what's going on in the first statement, the second statement may now make more sense. You may have thought it was comparing 0 < -1, which results in NO, and shouldn't produce a result of 1. What's actually being compared, is 0 < 18446744073709551615, which results in YES. This is why you're getting a result of 1.
It all boils down to using the incorrect format identifier in NSLog, which caused confusion on how to interpret the value.
It is always some version of nothing: nil, zero, NO.
The return type of count is NSUInteger as also pointed out by Joachim Isaksson in the comments. testArray.count-1 is expected to be -1, which in binary is encoded ...111111 (the exact number of bits depends on the platform, and whether the code is compiled as 32-bit or 64-bit). However, since the expression is unsigned it will be interpreted as a very large number instead - in fact the largest possible number that can be represented as an unsigned integer.
This very large number, when compared with the variable i is much bigger. Hence the output.

Objective-C NSNumber numberWithLongLong creates integer

When I attempt to create an NSNumber using the numberWithLongLong with a number greater than -2 and less than 13 it returns a number that is casted as an (int).
I see this if I look at the Xcode debugger after stepping over my line.
NSNumber* numberA = [NSNumber numberWithLongLong:-2]; //Debugger shows as (long)-2
NSNumber* numberB = [NSNumber numberWithLongLong:-1]; //Debugger shows as (int)-1
NSNumber* numberC = [NSNumber numberWithLongLong:12]; //Debugger shows as (int)12
NSNumber* numberD = [NSNumber numberWithLongLong:13]; //Debugger shows as (long)13
To put my problem in context, I am using a long long value for an epoch date that I will end up serializing using BSON and sending across the wire to a webservice. The webservice requires the date to be a java Long.
Thanks in advance
You have discovered that NSNumber (actually, its CFNumber counterpart) has a cache for integers between -1 and 12 inclusive. Take a look at the CFNumberCreate function in CFNumber.c to see how it works.
It looks like you can force it not to use the cache by passing your own allocator to CFNumberCreate. You'll need to look at the CFAllocator documentation.
But note that the CFNumberCreate manual says this:
The theType parameter is not necessarily preserved when creating a new CFNumber object.
So even if you bypass the cache, you might not get back an object whose objCType is q (which means long long). It looks like the current implementation will return q but that could change in a future version.
You are allowed to write your own NSNumber subclass if you need to guarantee that objCType returns q. Read “Subclassing Notes” in the NSNumber Class Reference.
You can use your webservice without concern.
NSNumber wraps a numeric value (of primitive type) as an object. How NSNumber stores that value is not really your concern (but there is a method to find it out), it is an opaque type. However NSNumber does maintain an internal record of the type used to create it so its compare: method can follow C rules for comparison between values of different types precisely.
For integral types the integral value you get back will be exactly the same, in the mathematical sense, as the one you created the NSNumber with. You can create an NSNumber with a short and read its value back as a long long, and the mathematical value will be the same even though the representation is different.
So you can store your integral date value as an NSNumber and when you read it back as a long long you will get the right value. No need to be concerned how NSNumber represents it internally, and indeed that could potentially change in the future.
(At least one implementation of NSNumber can store values as 128-bit integers, which helps ensure correct semantics for signed and unsigned integers. Also I stressed integral types as with the vagaries of real numbers talking about mathematical exactness is somewhat moot.)
Wait. I think I know what your asking. Try it this way:
NSNumber* numberA = [NSNumber numberWithLongLong:-2LL];
NSNumber* numberB = [NSNumber numberWithLongLong:-1LL];
NSNumber* numberC = [NSNumber numberWithLongLong:12LL];
NSNumber* numberD = [NSNumber numberWithLongLong:13LL];
BTW: it won't matter what the type of the constant is, it will be coerced into a long long when passed to [NSNumber numberWithLongLong:]
UPDATE
Based on #robmayoff's answer, I don't think NSNumber is reliable for your. How are you packing your BSON? is there a way to use NSValue instead of NSNumber?

Add integers from 5 UITextFields to a UILabel in Cocoa Touch

I'm trying to sum the integers from five UITextFields and post them to a UILabel.
This is the code I have tried, but it doesn't work properly. The number that shows up in the label is not the sum of my textfields. I have also tried to post to a textfield instead of a label, with the same result. No errors or warnings when I build.
int val = [textfield1.text intValue]
val = val+[textfield2.text intValue];
val = val+[textfield3.text intValue];
val = val+[textfield4.text intValue];
val = val+[textfield5.text intValue];
NSString *labelStr = [[NSString alloc] initWithFormat:#"%i", val];
label.text = labelStr;
Something wrong with the code? Alternative code? Grateful for all answers!
The code looks more or less right to me, aside from the memory leak. You should review the memory management rules and fix your leak.
My guess is that the numbers you entered add up to a number that is outside the range of an int. Entering, say, 1000000000 (10**9) in each of the five fields would be one way to pull this off, on any machine where an int is 32 bits (including, currently, the iPhone-OS devices).
Depending on the purpose of your app, you may be able to simply cap the five input fields; if the highest value that makes any sense is less than one-fifth (for five fields, and that's assuming they all have the same cap) of the maximum int, overflow is impossible.
If a cap won't solve the problem completely, try a different type. If the values should never be negative, use an unsigned type. Otherwise, try long long, or either of the floating-point types, or use NSDecimalNumber objects.
Of course, I could be completely wrong, since you didn't say what numbers you entered or what the result was. If it was zero, make sure that you hooked up your outlets in IB; if you forgot to do that, they contain nil, which, when you ask it for text, will return nil, which, when you ask it for an intValue, will return 0, and 0 + 0 + 0 + 0 + 0 = 0.

How to get double value from dictionary?

I'm trying to get a double value from a dictionary. How can I accomplish this in objective-c?
Dave's response to your previous question holds true for this, as well. To store a double value in an NSDictionary, you will need to box it in an NSNumber.
To set a double value in the dictionary, you'd use code like the following:
[someDict setObject:[NSNumber numberWithDouble:yourDouble] forKey:#"yourDouble"];
and read it back using the following:
double isTrue = [[someDict objectForKey:#"yourDouble"] doubleValue];
Brad Larson's response is exactly right. To elaborate on this a little more, you have to explicitly "wrap up" non-object number types (e.g., int, unsigned int, double, float, BOOL, etc.) into NSNumber when working with anything that expects an object.
On the other hand, however, some mechanisms in Objective-C, like Key-Value Coding (KVC), will automatically do this wrapping for you.
For example, if you have a #property of type int called intProperty, and you call NSObject (NSKeyValueCoding)'s valueForKey: method like [ someObject valueForKey:#"intProperty" ], the return result will be an NSNumber *, NOT an int.
Frankly, I don't care for having to switch between dealing with object and non-object types (especially structs and enums!) in Objective-C. I'd rather everything be treated as an object, but maybe that's just me. :)