check if value is integer or double in objective c - objective-c

I have a case where I should get the elements from an array and I do not know if the type is double or integer.
[array objectAtIndex:2]
and the problem is that I can not identify the type. If i knew the type I simply would perform:
[[item objectAtIndex:2] intValue] or
[[item objectAtIndex:2] doubleValue]
Is there any way to detect that?
Br,
Gzim

See the documentation for the -objCType method.
Basically, it returns a string describing the type contained in the NSNumber. The Objective-C runtime documentation has more information on the type codes.
However, it is quite odd to have a design where the type is unknown. I would suggest that a better solution -- possibly an interesting question -- would be to avoid the ambiguous type in the design of your application.

NSString* type = [NSString stringWithUTF8String:[valueInQuestion objCType]];
This will equal "d" if it is a double and "i" if it is an integer.

Related

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?

Errors in trying to cast to NSInteger * and NSMutableArray *

I'm a newbie in obj c. So I have a simple question.
I have a matrix of NSInteger values. It is called "curBoard". I want to update value at (x,y) coordinates with value "curStep". I have an arror "operand of type void where arithmetic..."
What am I doing wrong ?
[curBoard replaceObjectAtIndex:x withObject:(NSMutableArray *)[[curBoard objectAtIndex:x] replaceObjectAtIndex:y withObject:(NSInteger *)[NSNumber numberWithInt:curStep]]];
Update:
NSMutableArray *board;
board = [NSMutableArray new];
for(NSInteger i = 0; i<boardSize; i++) {
NSMutableArray *row = [NSMutableArray new];
for(NSInteger j = 0; j < boardSize; j++)
[row addObject:(NSInteger *)[NSNumber numberWithInt:0]];
[board addObject:row];
}
This withObject:(NSInteger *)[NSNumber numberWithInt:curStep]] part is what causing an issue. If you are storing as NSNumber objects, you should just use:
... withObject:[NSNumber numberWithInt:curStep]]
Edit:
From the code posted above, you should add it as:
[row addObject:[NSNumber numberWithInt:0]];
NSInteger is not of pointer type and you should use NSNumber itself to add to array.
Objective-C is basically just a bunch of object syntax strapped to C. The overall effect is something like strapping a jetpack to a horse: sometimes the two parts don't really work together very well. In this case, you're trying to go faster by telling the horse to giddy up, when you should really be opening up the throttle.
NSMutableArray is part of the jetpack—it's an Objective-C object and is only equipped to handle arrays of Objective-C objects. But NSInteger is part of the horse—it's a primitive C integer type, not a real object.*
I know NSInteger is capitalized like a class and has an NS prefix like a class, but it's really a creature of C. You can confirm this yourself—type Cmd-O in Xcode and type "NSInteger" into the Open Quickly dialog that pops up, and you'll be able to jump to its definition. In my current Mac project, that's typedef long NSInteger;; long is one of the primitive C types.
NSNumber exists to bridge the two. It's an object specifically designed to hold the C numeric types inside it. Since NSNumber is an object, NSMutableArray and other Objective-C things can deal with it.
But you can't just cast between NSNumber and NSInteger. NSNumber holds an NSInteger inside it, but that doesn't mean it's actually an NSInteger itself. If you put a sandwich in a plastic bag, you can't eat the bag.
Instead, you have to use NSNumber's +numberWithInteger: method to construct an NSNumber, and -integerValue to get the integer back out of it. (+numberWithInt: and -intValue will usually work, but they may behave differently with very large values, depending on whether your app is running on a 32-bit or 64-bit processor.) Actually, nowadays you can say [NSNumber numberWithInteger:foo] as #(foo) instead, which is a lot shorter.**
So when you add a number, you should be saying:
[row addObject:#(0)];
And when you later want that number back, you'll want to say something like:
n = [[row objectAtIndex:y] integerValue];
The -replaceObjectAtIndex:withObject: error is a different story. -replaceObjectAtIndex:withObject: doesn't return anything at all, so you can't use it as an argument. Luckily, you don't need to in this case. -replaceObjectAtIndex:withObject: doesn't create a new array; it alters the array that's already inside [curBoard objectAtIndex:x], so you don't need to do anything to curBoard. Instead, you can just write:
[[curBoard objectAtIndex:x] replaceObjectAtIndex:y withObject:#(curStep)];
* You actually used NSInteger *, which is slightly different. The * means "pointer to", so NSInteger * is a pointer to a primitive integer. This is sort of like NSNumber *, a pointer to an NSNumber object, so the compiler allows you to cast it.
Note that casting a pointer doesn't convert the data at the other end of the pointer; it just makes the compiler interpret the same data in a different way. If you actually tried to use the NSInteger * pointer to get data, you would either get garbage data or (for reasons too large to fit within this margin) crash.
In this case, though, once you've Jedi mind-tricked the compiler into thinking that value is a pointer to an NSInteger, you try to pass it to to -addObject:. -addObject: expects a pointer to an object, so the compiler balks at passing a pointer to an NSInteger instead.
** This syntax will work as long as you're using the iOS 6 SDK Xcode 4.4 or later, even if you actually run the app on an older iOS. It will also automatically use the right +numberWithWhatever: method for you, so you don't have to worry about picking the best one. When you're using a numeric literal like 0, the parentheses are optional, but they're required when you use a variable or constant. Of course, you can still do it the wordy way if you want, but there's little point nowadays.

NSDictionary and warning about "pointer from integer without a cast"

I see that there is a bunch of posts on this topic, but a solution is not clicking in my head. Here is my issue:
double var = [[variables objectForKey:[value characterAtIndex:1]] doubleValue];
variables is an NSDictionary. value in this situation is an NSString and I need characterAtIndex:1 because I prepended the string with a special designator. In English, I want to get a value from my NSDictionary based on this NSString key, but I get this warning when building:
warning: passing argument 1 of 'objectForKey:' makes pointer from integer without a cast
characterAtIndex: returns a unichar, not an object, while NSDictionary keys must be objects. It sounds like you want something like [value substringToIndex:1].

Objective-C for Dummies: How do I get a value out of NSDictionary?

so I'm having the most difficult of time pulling values out of an NSDictionary. Right now I just have a dictionary that is populated from a JSON call and it only contains a key named 'Success' with a value of 0 or 1.
How do I do a conditional on that value to check if its 0 or 1? I've tried a bunch of things, but I'm not getting anywhere. Here's my current code:
[[jsonDictionary objectForKey:#"Success"] isEqualToNumber:1]
I'm getting passing argument 1 of 'isEqualToNumber:' makes pointer from integer without a cast' as a warning, and the app crashes when it hits that line anyway.
And a subquestion, what's the difference between objectForKey and valueForKey? Which one should I use by default?
Anyway, this noob in Objective-C would truly appreciate some help on this. Thanks in advance!
Since dictionaries contain Objective-C objects, an entry containing a number is an NSNumber instance. NSNumber provides a convenience method, -intValue, for extracting its underlying int value:
if ([[jsonDictionary objectForKey:#"Success"] intValue] == 1) { … }
Note that NSNumber has other convenience methods for extracting its underlying value as other C data types.
In most cases, you should use -objectForKey: instead of -valueForKey:. The former is the canonical method to obtain an entry in the dictionary and is declared in NSDictionary. The latter is declared in NSObject and is used in Key-Value Coding contexts, where the key must be a valid KVC key, and there’s additional processing — for instance, if you’re using -valueForKey: in a dictionary with a key that starts with #, that character is stripped from the key and [super valueForKey:key] is called.
The number 1 is not an object pointer. Use an NSNumber instance instead if you want to use a number in an NSDictionary.
[[jsonDictionary objectForKey:#"Success"]
isEqualToNumber:[NSNumber numberWithInteger:1]]
[[jsonDictionary objectForKey:#"Success"] isEqualToNumber: [NSNumber numberWithInt:1]]
Number and Value Programming Topics: Using Numbers
NSNumber: What is the point ?
You can get the value of dictionary in different ways like checking
the value first.
Solution 1: Using simple if statement.
int value = 0;
if ([[jsonDictionary objectForKey:#"Success"]intValue]==1){
value = [[jsonDictionary objectForKey:#"Success"]intValue];
}
Solution 2: Using ternary operator
value = ([[jsonDictionary objectForKey:#"Success"]intValue]==1) ? 1:0;

How to convert an pointer to become a object?

I using something like this : [tmpArray insertObject:something[i] atIndex:i];
But I got a MSG : passing argument 1 of "insertObject:atIndex:" makes pointer from integer without a cast.
What should I do to fix it?
I guess your something[] array is a C array of int, if this is the case you cannot pass its value at that position into an NSArray since it contains objects (and objects in obj-C are referenced by pointers).
What you could do is wrap the int into a NSNumber this way:
[tmpArray insertObject:[NSNumber numberWithInt:something[i]] atIndex:i];
You can't magically make random pointer into an object, you need to understand what the type of the pointer is and do something semantically reasonable with that type. Also, that is not what the error you are seeing is about. The error is because the type expected is a pointer (and an object in ObjC is also a pointer type), and the type info of the thing you are passing is a scalar.
Taking a wild guess, something is probably an array of ints, if so what you want to do is wrap is the integer in an NSNumber in order to store it in a collection:
//assuming this:
int something[100];
//then your line should be this:
[tmpArray insertObject:[NSNumber numberWithInt:something[i]] atIndex:i];
Obviously you will need to unbox the value anywhere you access it. If it is not an array of integers then you will need to do something else (though probably similiar), but without more info I can't tell you exactly what.