Formatting NSUInteger with %i - ios

I am going through a former employees code and about 20 of these warnings show up:
Values of type 'NSUInteger' should not be used as format arguments; add an explicit cast to 'unsigned long' instead
one part of the code where this arises is:
NSUInteger Length;
With:
- (NSString *) description {
// If no value was given, display type
if([Value length] == 0)
{
NSString *type = #"";
switch (Type) {
case DTCompType_Year: type = #"year";
break;
case DTCompType_Month: type = #"month";
break;
case DTCompType_Day: type = #"day";
break;
case DTCompType_Hour: type = #"hour";
break;
case DTCompType_Minute: type = #"minute";
break;
case DTCompType_Second: type = #"second";
break;
case DTCompType_Meridiem: type = #"meridiem";
break;
case DTCompType_MonthName: type = #"month_name";
break;
case DTCompType_DayOfTheWeek: type = #"day_of_the_week";
break;
case DTCompType_Undefined: type = #"undefined";
break;
}
return Length == 0 ? [NSString stringWithFormat:#"[%#]", type] :
[NSString stringWithFormat:#"[%#:%i]", type, Length];
}
No where in apples documentation can I find %i
Apple's Documentation
I have never worked with Objective-C before, and now I have to update this app. I understand that this needs to become an unsigned long, but I don't want to start changing things without knowing why. The app works just fine as is, so are there any inherent consequences for changing these to unsigned long? or even changing the format specifier from %i to %lu?
From what I've read, it could be a matter of the platform. (32-bit vs 64-bit)
This was developed for an iPad 2 in iOS7, and we just upgraded the SDK to iOS8.
I found this post:
NSUInteger should not be used in format strings?
which has given me some guidance, but I need more clarification.

%i is equivalent to %d. Technically, you should have been using %u anyway. The problem is, as you suspect, 32-bit vs 64-bit; NS[U]Integer is [unsigned] int on 32-bit builds, but [unsigned] long on 64-bit ones. Because the iPhone is little-endian, it will "work" as long as the %i/d/u is the last format specified, but it's still wrong. You should cast the argument to be the type the format specifier expects (int/long/unsigned/unsigned long), as the warning message tells you to.
From <objc/NSObjCRuntime.h>:
#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif

You can use a boxed literal to allow the compiler and the NSNumber class to handle the details of converting between the various numeric types and their string representations. For example, given the following variable definition...
NSUInteger foo = 42;
...you can create an instance of NSNumber as follows:
NSNumber *myNumber = #(foo);
You can then use the %# format specifier whenever you need to format the value of myNumber. Of course it's easy enough to instead box the original numeric value right in line:
NSString *s = [NSString stringWithFormat:#"The answer is %#", #(foo)];

Related

iOS 64Bit converting dont work in a method with va_list

I am trying to make an iOS-app ready for 64bit.
I got a method which build me a string with entries from an enum. The parameters from this method can be variable in count.
The method works fine under 32bit, but under 64bit my for-loop cant end correctly.
Here some code from .h:
#define enumToString(intVal) \
[NSString stringWithFormat: #"%ld", intVal]
#define ENUM_END -1
typedef enum _MYENTRIES
{
entry1,
entry2,
entry3
} MYENTRIES;
typedef NSUInteger MYENTRY;
My crashing method: (the loop has to end, but it doesn't end)
-(NSString*) getMyString:(MYENTRY) firstArg, ... {
va_list args;
va_start(args, firstArg);
NSMutableString *mySTRING = [[[NSMutableString alloc] init] autorelease];
for (MYENTRY arg = firstArg; arg != ENUM_END; arg = va_arg(args, MYENTRY))
{
NSLog(#"arg: %d %#", arg, enumToString(arg)); // when ENUM_END: "arg: -1 4294967295"
[mySTRING appendString:self.myDictionary[[NSString stringWithFormat: #"%ld", arg]]];
}
An example methodcall:
myString = [myClass getMyString: entry1, entry3, ENUM_END , nil];
Hope you can help me.
best regards
That's because MYENTRIES and MYENTRY are different types. One is 32 bit, and the other is 64 bit. Passing 32 bit values and reading them as 64 bit isn't going to work.
That's what the NS_ENUM macro is there for. Google for it, understand it, and use it. You will also have the problem that ENUM_END is not the same type as either MYENTRIES or MYENTRY (it is an int). Unless you can quote the rules for type conversion between int and unsigned long by heart (which you can't), I suggest you make it part of the enum.

Why isn't NSInteger defined as long on 32-bit iOS?

NSInteger is defined this way:
#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
#else
typedef int NSInteger;
#endif
This results in NSInteger being defined as int on 32-bit iOS even though int and long are the same anyway (both 4 bytes). Format strings like the following generate a warning with this definition:
NSInteger x = 4;
[NSString stringWithFormat: #"%ld", x];
// Warning: Values of type 'NSInteger' should not be used as format arguments;
// add an explicit cast to 'long' instead.
So does somebody know why NSInteger isn't always defined as long?
Historical reasons, where previous releases of APIs used int -- then were migrated to use typedef NSInteger ca. the 64-bit transition of OS X.
I suppose they could have changed for iOS, but that would have impacted a lot of existing and to-be-developed code if they were different on OS X and iOS.

Pass a variable to decimalNumberHandlerWithRoundingMode?

Forgive me if I use the wrong terminology as I'm still a little new at iOS development. I've built a calculator-type app and I want users to be able to control how numbers are rounded. Here's the code I'm using:
-(NSString*)calculateWidthFromHeightString:(NSString*)height usingDimensions:(Favorite*)dimensions{
int decimalPlaces = [self.userData.rounding intValue];
NSUInteger *roundingMethod;
if ([self.userData.roundingMode isEqualToString:#"up"]) {
roundingMethod = NSRoundUp;
}
else if ([self.userData.roundingMode isEqualToString:#"plain"]) {
roundingMethod = NSRoundPlain;
}
else {
roundingMethod = NSRoundDown;
}
NSDecimalNumberHandler *handler = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:roundingMethod
scale:decimalPlaces
raiseOnExactness:NO
raiseOnOverflow:NO
raiseOnUnderflow:NO
raiseOnDivideByZero:NO];
This works as expected, but I'm getting the following compiler warning where I assign the rounding mode to the pointer "roundingMethod":
Incompatible Integer to pointer conversion assigning to ‘NSUInteger *’
(aka ‘unassigned long *) from ‘NSUInteger’ (aka ‘unassigned long’)
Incompatible Integer to pointer conversion assigning to ‘NSUInteger *’
(aka ‘unassigned int *) from ‘NSUInteger’ (aka ‘unassigned int’)
I don't really know what this means. Any help would be greatly appreciated.
This line:
NSUInteger *roundingMethod;
should be:
NSUInteger roundingMethod;
NSUInteger is a native type, not a class type.

Converting int to NSString

I thought I had nailed converting an int to and NSString a while back, but each time I run my code, the program gets to the following lines and crashes. Can anyone see what I'm doing wrong?
NSString *rssiString = (int)self.selectedBeacon.rssi;
UnitySendMessage("Foo", "RSSIValue", [rssiString UTF8String] );
These lines should take the rssi value (Which is an NSInt) convert it to a string, then pass it to my unity object in a format it can read.
What am I doing wrong?
NSString *rssiString = [NSString stringWithFormat:#"%d", self.selectedBeacon.rssi];
UPDATE: it is important to remember there is no such thing as NSInt. In my snippet I assumed that you meant NSInteger.
If you use 32-bit environment, use this
NSString *rssiString = [NSString stringWithFormat:#"%d", self.selectedBeacon.rssi];
But you cann't use this in 64-bit environment, Because it will give below warning.
Values of type 'NSInteger' should not be used as format arguments; add
an explicit cast to 'long'
So use below code, But below will give warning in 32-bit environment.
NSString *rssiString = [NSString stringWithFormat:#"%ld", self.selectedBeacon.rssi];
If you want to code for both(32-bit & 64-bit) in one line, use below code. Just casting.
NSString *rssiString = [NSString stringWithFormat:#"%ld", (long)self.selectedBeacon.rssi];
I'd like to provide a sweet way to do this job:
//For any numbers.
int iValue;
NSString *sValue = [#(iValue) stringValue];
//Even more concise!
NSString *sValue = #(iValue).stringValue;
NSString *rssiString = [self.selectedBeacon.rssi stringValue];
For simple conversions of basic number values, you can use a technique called casting. A cast forces a value to perform a conversion based on strict rules established for the C language. Most of the rules dictate how conversions between numeric types (e.g., long and short versions of int and float types) are to behave during such conversions.
Specify a cast by placing the desired output data type in parentheses before the original value. For example, the following changes an int to a float:
float myValueAsFloat = (float)myValueAsInt;
One of the rules that could impact you is that when a float or double is cast to an int, the numbers to the right of the decimal (and the decimal) are stripped off. No rounding occurs. You can see how casting works for yourself in Workbench by modifying the runMyCode: method as follows:
- (IBAction)runMyCode:(id)sender {
double a = 12345.6789;
int b = (int)a;
float c = (float)b;
NSLog(#"\ndouble = %f\nint of double = %d\nfloat of int = %f", a, b, c);
}
the console reveals the following log result:
double = 12345.678900
int of double = 12345
float of int = 12345.000000
original link is http://answers.oreilly.com/topic/2508-how-to-convert-objective-c-data-types-within-ios-4-sdk/
If self.selectedBeacon.rssi is an int, and it appears you're interested in providing a char * string to the UnitySendMessage API, you could skip the trip through NSString:
char rssiString[19];
sprintf(rssiString, "%d", self.selectedBeacon.rssi);
UnitySendMessage("Foo", "RSSIValue", rssiString );

How do I use a core data Integer 64 property?

I want to have an Entity property in Core Data be a 64-bit integer. Since the model is going to run on iOS, and as far as I know these devices are not 64-bit, I figured that NSNumber was the way to go (core data gives you the option of objects or scalar properties for primitive types).
I'm assuming that NSNumber will internally take care of keeping track of a suitable representation for 64 bits.
Now, I need to subtract 1 from this "64 bit" property in my entity at some point (in case you didn't guess, the 64 bit property is the max_id parameter in the Twitter API), but to do so, I first need to unbox the number inside the NSNumber property.
So should i get the intValue? longValue? unsignedIntValue? unsignedLongValue? long long? which one?
Since you already know the type (64 bit integer), you don't need to check for it.
To get a 64 bit integer out of a NSNumber, do one of the following:
NSInteger myInteger = [myNSNumber integerValue];
int64_t myInteger = [myNSNumber integerValue];
In order to just add one to it, you can use something like this:
myNSNumber = [NSNumber numberWithInteger:[myNSNumber integerValue]+1]];
Note that iOS does have 64 bit data types like int64_t and NSInteger.
EDIT:
If the only reason that you are using NSNumber is to store the 64 bit integer, you can just declare the property like this in your model subclass and skip the unboxing/boxing altogether:
#property (nonatomic) int64_t myIntValue;
Note that core data does this by default if you select the Use scalar properties for primitive data types option of the Create NSManagedObject Subclass feature.
Try putting this in a NSNumber category:
-(int64_t) int64value
{
if (sizeof(short) == 8)
return [self shortValue];
if (sizeof(int) == 8)
return [self intValue];
if (sizeof(long) == 8)
return [self longValue];
if (sizeof(long long) == 8)
return [self longLongValue];
return -1; // or throw an exception
}
To get the C type contained in NSNumber use objCType
Example
NSNumber *myFloat = [NSNumber numberWithFloat:5.5f];
NSLog(#"%s", [myFloat objCType]);
Will print "f" as it contains a value of type float.
Also, check out #encode() which will return a C type character.
Example
NSNumber *myFloat = [NSNumber numberWithFloat:5.5f];
if (strcmp(myFloat) == #encode(float)) {
NSLog(#"This is a float");
}
Also
NSNumber *myFloat = [NSNumber numberWithFloat:5.5f];
CFNumberType numberType = CFNumberGetType((CFNumberRef)myFloat);

Resources