Why doesn't this please the compiler? Casting is supposed to work like in C as I can read here How to cast an object in Objective-C.
[p setAge:(NSNumber*)10];
where
- (NSNumber*) age {
return _age;
}
- (void) setAge: (NSNumber*)input {
[_age autorelease];
_age = [input retain];
}
In a sort of symmetry with your earlier question, NSNumber is an object type. You need to create it via a method invocation such as:
[p setAge:[NSNumber numberWithInt:10]];
Your current code is attempting simply to cast the arbitrary integer 10 to a pointer. Never do this: if the compiler didn't warn you about it, you would then be trying to access some completely inappropriate bytes at memory location 10 as if they were an NSNumber object, which they wouldn't be. Doing that stuff leads to tears.
Oh, and just to preempt the next obvious issues, remember that if you want to use the value in an NSNumber object, you need to get at that via method calls too, eg:
if ( [[p age] intValue] < 18 )
...
(NSNumber is immutable, and I think it is implemented such that identical values are mapped to the same object. So it is probably possible to get away with direct pointer comparisons for value equality between NSNumber objects. But please don't, because that would be an inappropriate reliance on an implementation detail. Use isEqual instead.)
Today it is also possible to do it using shorthand notation:
[p setAge:#(10)];
Use this:
[p setAge:[NSNumber numberWithInt:10]];
You can't cast an integer literal like 10 to a NSNumber* (pointer to NSNumber).
NSNumber *yourNumber = [NSNumber numberWithInt:your_int_variable];
Because NSNumber is an object and "10" in a primitive integer type, much like the difference between int and Integer in Java. You, therefore, need to call its initialiser:
[p setAge:[NSNumber numberWithInt:10]
Related
I am making a calculator that logs input in a label named "inputLabel' and then outputs the answer in a different label named "outputLabel" (similar to a graphing calculator). Once the user is finished entering the expression, the expression is stored in an NSString object and then parsed with the NSPredicate class and evaluated with the NSExpression class. What I have works, but I have noticed for particular operations the answers are not correct. For example, if the user types in "25/2" the calculator returns 12, which is obviously incorrect. However, if the user types in "25/2.0" or "25.0/2" the calculator returns 12.5 which is what I want. It seems that the NSExpression method 'expressionValueWithObject' is interpreting the operands as integers instead of floats. If this is the case, is there a way that I change the 'expressionValueWithObject'method to interpret the operands as floats?
Brain.m
-(float)performCalculation: (NSString *)operation
{
NSPredicate *parsed = [NSPredicate predicateWithFormat:[operation stringByAppendingString:#"=1.0"]];
NSExpression *inputExpressionParsed = [(NSComparisonPredicate *)parsed leftExpression];
NSNumber *result = [inputExpressionParsed expressionValueWithObject:inputExpressionParsed context:nil];
return [result floatValue];
}
ViewController.m
- (IBAction)equalsPressed:(id)sender
{
//self.inputLabel.text = [self.inputLabel.text stringByAppendingString:#".0"];
NSString *inputExpression = self.inputLabel.text;
self.inputLabel.text = [self.inputLabel.text stringByAppendingString:#"="];
float result = [self.brain performCalculation:inputExpression];
self.outputLabel.text = [NSString stringWithFormat:#"%g", result];
}
No, NSExpression cannot do that. You could try to append ".0" to all integer numbers
in the string before evaluating it, but the better solution is probably to use a "proper"
math expression parser, for example
https://github.com/davedelong/DDMathParser
You could iterate through the expression tree replacing the expression with the integer value (expressionType == NSConstantExpression). It depends a little bit of the features of your calculator, whether it is worth or not.
Something weird just happened.
I stored a NSNumber with an unsigned long long value in NSUserDefaults. When I retrieve it, the value just changed. It seems that system thinks the number is long long instead of unsigned long long.
What's worse is that when I compare the number retrieved from UserDefaults with the original number, the result is NotEqual!
what's wrong with the code? Thank you!
static NSString * const NumberKey = #"MyNumber";
unsigned long long value = 15908045869032883218ULL;
if ([[NSUserDefaults standardUserDefaults] objectForKey:NumberKey] == nil) {
NSNumber *number = [NSNumber numberWithUnsignedLongLong:value];
[[NSUserDefaults standardUserDefaults] setObject:number forKey:NumberKey];
NSLog(#"Original Number:%#", number); // 15908045869032883218, right
}
NSNumber *number = [[NSUserDefaults standardUserDefaults] objectForKey:NumberKey];
NSLog(#"Current Number:%#", number); // -2538698204676668398, weird
NSLog(#"Current Value:%llu", [number unsignedLongLongValue]); // 15908045869032883218, right
NSLog(#"%d", [number isEqualToNumber:[NSNumber numberWithUnsignedLongLong:value]]); // 0
NSLog(#"%d", [number unsignedLongLongValue] == value); // 1
To further answer your question. If you look in the documentation for NSNumber's isEqualToNumber: function you will notice the following line,
Two NSNumber objects are considered equal if they have the same id values or if they have equivalent values
it's important you understand this. In your code you are asking is my NSNumber object "number" equal to "value", you are not asking does the numerical value stored within my NSNumber object "number" equal the numerical value stored within my NSNumber object "value".
The last line of code you have written shows that in fact your NSNumber's numerical values are in fact equal.
NSLog(#"%d", [number unsignedLongLongValue] == value); //1
So you are correctly storing and retrieving the values, you should be using the == comparison method with NSNumber objects stored numerical values (ie intValue == intValue, unsignedLongLongValue == unsignedLongLongValue) and not comparing their object id's together.
As for this line of code
NSLog(#"Current Number:%#", number); // -2538698204676668398, weird
This is not weird, this is perfectly normal, as you have told NSLog to print out an NSObject representation of 'number'. I'm not 100% certain but I believe that NSNumber's - ( NSString * ) description function defaults to return an unsigned int value for the numerical value it contains. This is why you are getting the large negative number returned. You may want to look at NSNumber's - (NSString *)descriptionWithLocale:(id)aLocale function to print out the data in a more logical for for you, or you could use
NSLog(#"Current Number:%llu", [number unsignedLongLongValue]);
Which will give you the right answer.
EDIT:
Further to this, after looking into the issue what is happening is that on recollection of your NSNumber object from UserDefaults it's original number type is not being preserved (this information is highlighted in the documentation for NSNumber in the overview section)
(Note that number objects do not necessarily preserve the type they are created with.)
You can see this yourself if you log the following after retrieving "number" from user defaults (add this to the end of the code you have in your question) and have a look at the encoding values shown here
NSLog(#"%s", [number objCType]); //This will log out q
NSLog(#"%s", [[NSNumber numberWithUnsignedLongLong:value] objCType]); //this will log out Q
The difference between Q and q is that Q is an unsigned value... hence why you are having issues with the isEqualToNumber: function as the number types are different.
If you are so dead set on using the iSEqualToNumber: function to compare values then you could implement this to retrieve your value from NSUserDefaults.
NSNumber *number = [NSNumber numberWithUnsignedLongLong:[[NSUserDefaults standardUserDefaults] objectForKey:NumberKey] unsignedLongLongValue]];
You could look at using the NSNumber compare: function to see if the returned value is NSOrderedSame however this will not work for comparing unsigned vs signed values of the same type so in your situation I'd use the above as retrieving the data from NSUserDefaults is stripping the "signedness" of your number.
At the end of the day if you want to store NSNumber into NSUserDefaults this code works for me even for large integers like: 881217446193276338
To save:
[[NSUserDefaults standardUserDefaults] setObject:self.myUser.sessionid forKey:#"sessionid"];
[[NSUserDefaults standardUserDefaults] synchronize];
To recover:
self.myUser.sessionid = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:#"sessionid"];
It's storing it correctly, nothing is wrong with your code except:
NSLog(#"Current Number:%#", number);
Here number is a non-string object, you might think of it as a wrapper for a numerical primitive. Or you might think that NSNumber instances objectify a primitive type.
What you need is some thing like:
NSLog(#"Current Number:%#", [number stringValue]);
Here is a speculative answer:
The NSNumber documentation states that:
(Note that number objects do not necessarily preserve the type they are created with.) .
So it must be using a different internal storage for this type, and only gives you the correct value when you specifically ask for the unsigned long long value. The description method, which is called in your NSLog statement, may be defaulting to a different representation type.
And / or there may be some quirk of the unarchiver that is preventing the isEqualToNumber method working on the value returned from defaults. If you do that comparison between two NSNumbers created in the same scope, does it work? The correct value is definitely in there somewhere given your last statement returns true.
I WANT to use NSinteger variable *strength in my code with if condition but it's not work.. :(
if(strength == 11){
}
How can i use if with NSInteger*
NSInteger is a primitive value type; you don't really need to use pointers. So your declaration should read
NSInteger strength;
And not
NSInteger *strength;
However if you do need to use a pointer to an NSInteger (that is, NSInteger *) for some reason, then you need to dereference the pointer to get the value:
if (*strength == 11) {
}
but from what I see, I don't think this is the case.
I assume you must be adding an * when you declare your strength variable. You shouldn't have it because NSInteger is a primitive type.
Why don't I declare NSInteger with a *
I have 2 NSArray (Mutable, actually) that I am trying to convert to a C-style double array for a c routine i am passing them to.
Here is my Objective-C routine:
NSMutableDictionary *childDictionary = [myParentDictionary objectForKey:resort_code];
latitudeArray = [childDictionary objectForKey:#"lat"];
longitudeArray = [childDictionary objectForKey:#"lon"];
int nvert = [latitudeArray count];
double laArray[nvert];
double loArray[nvert];
for(int i=0; i<nvert; i++) {
double dLat = [[latitudeArray objectAtIndex:i]doubleValue];
double dLon = [[longitudeArray objectAtIndex:i]doubleValue];
laArray[i] = dLat;
loArray[i] = dLon;
}
This takes upwards of 3-8 seconds on the 3G iPhone (instantaneous on the simulator -- yet another reason to test on the device )
is there faster way? I have to end up with laArray[i] and loArray[i] as c-style arrays of doubles.
(to expand on the question for the benefit of a commenter):
Each array consists of #"38.448745" (lat) and #"-122.9847684" (lon) style content. I do this cos to be pushed onto an NSArray, the lat and lon need to be objects. I simply used:
[latitudeArray addObject:[NSString stringWithFormat: #"%.10f",dlat]];
[longitudeArray addObject:[NSString stringWithFormat: #"%.10f",dlon]];
I suppose I could change that to:
[latitudeArray addObject:[NSNumber numberWithDouble: #"%.10f",dlat]];
[longitudeArray addObject:[NSNumber numberWithDouble: #"%.10f",dlon]];
...which may reduce the conversion time of
double dLat = [[latitudeArray objectAtIndex:i]doubleValue];
but wouldn't I still need that exact line to convert from NSString to double? It just may work faster?
thx
dlat is a double, right?
So instead of:
[latitudeArray addObject:[NSString stringWithFormat: #"%.10f",dlat]];
Do:
[latitudeArray addObject:[NSNumber numberWithDouble:dlat]];
They both respond to doubleValue but the NSNumber should not have to do any string parsing since it's stored as a numeric value already. And you never have to go to a string at all.
I suspect you have an array of strings like #"213.12385" that need to be parsed and converted when you call doubleValue on them. If that is where the issue is, the C arrays have nothing to with this.
Only thing I would add here is to throw Shark on this and see where it's spending it's time. If it's spending time in doubleValue find a different way to parse the strings with preprocessing in background or something. If it's in objectAtIndex: perhaps fast enumeration would help. If it's somewhere else entirely then you know it's not this snippet that's slow.
For the general case of converting an NSArray to a C array, you can use getObjects:. In this case, though, want you actually want is not to convert the NSArray, but to derive an array of doubles from an NSArray of some unspecified object type.
One obvious way to speed things up would be fast enumeration rather than sending a message to get the object for each iteration of the loop. I suspect the real solution, though, is outside your algorithm. The slowness probably comes from transforming whatever objects the array contains into doubles, in which case you'll need to find a way around that — maybe store doubles all along, maybe do the conversion in the background, etc. If you're creating the arrays yourself and there isn't some compelling reason for the objects to be strings, you should use NSNumbers instead. Those should be quite a bit faster.
The best solution is probably to make sure those values never end up in an NSArray as NSString values. I would attack this at the source.
So you edited your question and added that you are actually building those arrays. So why not use native arrays of doubles or floats from the start? I usually recommend against this but in your case it sounds like there is a huge performance gain.
Possibly using fast iteration, but I doubt that will really speed up your loop.
I've got an NSDictionary that was initialized with a plist that, among other things, contained count followed by 32 and I want to get at the value for count.
How?
I know how to get it into an object via
[dictionaryObjectName objectForKey:#"count"]
But to me, the value I'm obtaining is not an object.
If I have to specify an object, what would be the best one to use (FYI the value will always be unsigned) and I need to get it into a true int.
Do I do something like
NSNumber *num = [dictionaryObjectName objectForKey:#"count"];
int theValue = [num intValue];
[num release];
Is the release on num a good thing to do since this for an iPhone with no garbage collector?
And nicer form is:
int theValue = [[dictionaryObjectName objectForKey:#"count"] intValue];
EDIT
Since I see that people still arrive to this page, let's clarify that these days you simply do
int theValue = [dictionaryObjectName[#"count"] intValue];
Yes, you pull it out as an NSNumber, then grab the int(eger)Value but there's no need to release it. You didn't retain, alloc, or copy the number, so you don't have to worry about releasing it.