Changing value of a NSNumber - objective-c

Is there a way to change the value contained in an NSNumber after it is created without making it point to a different NSNumber?
NSNumber *num = [NSNumber numberWithInt:0];
num = [NSNumber numberWithInt: 1]; // now num points to a different object, which I don't want. I want it the same object still, but different value.

NSNumber is immutable. Actually, it's a subclass of NSValue, and all NSValues are immutable.

No, you can't change the value of NSNumber.
See, for example, this post.

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.

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).

Is it possible to cast an NSInteger to NSNumber?

Is it possible to cast a NSInteger to a NSNumber object?
I need to convert the tag of a UIImageView object to a NSNumber object because I need to pass it as an argument to a function.
You cannot cast it because NSInteger is not an object, just an alias for a built-in type. You can always create a new NSNumber object from NSInteger, like this:
NSNumber *myNum = #(myNsIntValue);
or in the prior version of the compiler, use
NSNumber *myNum = [NSNumber numberWithInteger:myNsIntValue];
since Apple LLVM Compiler 4.0, there is an easier way to create NSNumber object:
NSNumber *x = #1234;
NSNumber *y = #(anIntegerVariable);
This is the more correct answer and it will not produce unexpected error.
NSNumber *myNum = [NSNumber numberWithInteger:myNsIntValue];
Because the doc said:
"numberWithInteger:
Creates and returns an NSNumber object containing a given value, treating it as an NSInteger."
"numberWithInt:
Creates and returns an NSNumber object containing a given value, treating it as a signed int."

Sum NSArray of NSDecimalNumbers

I know you can sum an array of NSNumbers by using the #sum.self keypath, but does that also work with NSDecimalNumbers? Will the result be accurate?
EDIT: To be more specific, here is code I know that works with NSNumber.
NSNumber *sum = [numArray valueForKeyPath:#"#sum.self"];
NSDecimalNumber is a subclass of NSNumber, so it will inherit this ability.
Also, I would recommend using one of these:
NSDecimalNumber *sum = [numArray valueForKeyPath:#"#sum.floatValue"];
float sum = [[numArray valueforKeyPath:#"#sum.floatValue"] floatValue];

How to Properly set an integer value in NSDictionary?

The following code snippet:
NSLog(#"userInfo: The timer is %d", timerCounter);
NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:timerCounter] forKey:#"timerCounter"];
NSUInteger c = (NSUInteger)[dict objectForKey:#"timerCounter"];
NSLog(#"userInfo: Timer started on %d", c);
produces output along the lines of:
2009-10-22 00:36:55.927 TimerHacking[2457:20b] userInfo: The timer is 1
2009-10-22 00:36:55.928 TimerHacking[2457:20b] userInfo: Timer started on 5295968
(FWIW, timerCounter is a NSUInteger.)
I'm sure I'm missing something fairly obvious, just not sure what it is.
You should use intValue from the received object (an NSNumber), and not use a cast:
NSUInteger c = [[dict objectForKey:#"timerCounter"] intValue];
Dictionaries always store objects. NSInteger and NSUInteger are not objects. Your dictionary is storing an NSNumber (remember that [NSNumber numberWithInteger:timerCounter]?), which is an object. So as epatel said, you need to ask the NSNumber for its unsignedIntegerValue if you want an NSUInteger.
Or like this with literals:
NSUInteger c = ((NSNumber *)dict[#"timerCounter"]).unsignedIntegerValue;
You must cast as NSNumber first as object pulled from dictionary will be id_nullable and so won't respond to the value converting methods.