can't use array.count any longer - objective-c

now, when I try to nslog my array count using self.array.count with %d I receive this messageL:Values of type "NSUInteget" Should be not be used as format argumments and it suggest s that I fix it with %lu instead,. is this documented anywhere?

NSArray's count method returns an NSUInteger, as documented. If you're using a 64 bit environment, it's also documented that those require the format of %lu.
If you were using a signed NSInteger instead, you would need to use %ld in a 64 bit environment, or %d in a 32 bit environment.

You are probably seeing the message now because you have updated your compiler, Xcode is doing more checking and issuing more warnings.
The issue here is that the size of NSUInteger (and NSInteger) is different on different platforms. Rather than add a new format specifier to handle this Apple choose to recommend that the format specifier for the largest possible type is used and a cast. So:
For NSUInteger use the format specifier %lu and cast the value to unsigned long; and
For NSInteger use the format specifier %ld and cast the value to long.
Doing this will produce correct results on both 32-bit and 64-bit platforms.
See Platform Dependencies in String Format Specifiers.

Related

Xcode warning: Format specifies type 'long' but the argument has type 'int _Nullable'

I get this warning for the following line of code:
NSLog(#"selected segment: %li", _segmentControl.selectedSegmentIndex);
The property selectedSegmentIndex is of type NSInteger.
If I change the format to %i i get the following warning:
Format specifies type 'int' but the argument has type 'long _Nullable'
Are there any new format specifiers for Nullable types or is this just a bug in Xcode 7?
You should type:
NSLog(#"selected segment: %li", (long)_segmentControl.selectedSegmentIndex);
Because NSInteger has a different length in 32 and 64bit architecture. Previously you didn't see the warning, because probably you was compiling only against 64 bit architecture.
I'd also advise to read Apple Article, as there are new specifiers in Xcode 7 (among others nullable and nonnull).
To answer your doubts from the comment, please refer to this Apple document, where they state the following:
Type Specifiers
Script action: Warns about potential problems; may generate false negatives.
Typically, in 32-bit code you use the %d specifier to format int
values in functions such as printf, NSAssert, and NSLog, and in
methods such as stringWithFormat:. But with NSInteger, which on 64-bit
architectures is the same size as long, you need to use the %ld
specifier. Unless you are building 32-bit like 64-bit, these
specifiers generates compiler warnings in 32-bit mode. To avoid this
problem, you can cast the values to long or unsigned long, as
appropriate. For example:
NSInteger i = 34;
printf("%ld\n", (long)i);
Just want to add :
I got this warning "suddenly" even when I did not change the code that made the warning for long time, and I did not understand why it's appear.
The reason : "Generic iOS Device".
when selected device or simulator, the warnings disappear.
still, I add to the NSLog "(long)" to the variables. till then I only had "%ld", that turn the warning no matter what was selected (generic device, real device, simulator)

How to remove 100s of warnings "implicit conversion loses integer precision: 'NSInteger' (aka 'long') to 'int'" I got after updating to arm64?

Problem:
Yesterday I converted a large project of mine to support arm64 and after that I got 500+ warnings at once. About 70% of them are where NSInteger is being assigned to int or vice versa, and remaining are where NSUInteger is formatted in NSString like this:
NSInteger a = 123;
NSString *str = [NSString stringWithFormat:#"Int:%d", a]; //warning: value of 'NSInteger' should not be used as formate argument; add an explicit cast to 'unsigned long' instead.
Now I do know how to adress them manually, but that's a huge task and very laborious.
I'm also aware that I can silence the type mismatch warnings all together, but I don't want to do that. Of course, they're very helpful.
What I've tried:
I've converted [NSNumber numberWithInt:abc]; to [NSNumber numberWithInt:(int)abc]; using find-n-replace. It fixed some.
I've also tried to change all my int properties to NSInteger properties
but it doubled the number of warnings (reached to 900+ count). So I
reverted.
I've also tried to find some regular expression but couldn't find
something suitable to my needs.
Question:
I'm looking for a regular expression or any other workaround somebody has tried which can reduce the amount of work needed to fix them manually.
Thanks in advance.
NSInteger a = 123;
NSString *str = [NSString stringWithFormat:#"Int:%ld", (long)a];
After updating to 64 bit need to do typecast like this((long)a). %d is only for 32 bit range %ld for long integer. For better understanding got through this apple documentation.
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Cocoa64BitGuide/ConvertingExistingApp/ConvertingExistingApp.html
In case someone else's facing a similar situation, I want to clarify how to deal with it. Although #Raju's answer is suggesting to do it manually (which I wanted to avoid), I found exactly what I needed at the link he shared.
Apple has provided a script for 64bit conversion called ConvertCocoa64, located at/Developer/Extras/64BitConversion/ConvertCocoa64 which not only converts all int to NSInteger it also deals with float to CGFloat conversion, as stated:
It converts most instances of int and unsigned int to NSInteger and
NSUInteger, respectively. It doesn't convert ints in bit-field
declarations and other inappropriate cases. During processing, the
script refers to a hardcoded list of exceptions.
In addition to above conversions it also flags the lines in code which need manual fix. So this might help with the warnings of String Formats.
Please refer to this link for complete details. It not only explains how to use the script but also suggests some very important post 64-bit migration check points.
objective c implicit conversion loses integer precision 'NSUInteger' (aka 'unsigned long') to 'int
Change key in Project > Build Setting "implicit conversion to 32Bits Type > Debug > *64 architecture : No"
Other warning
Change key in Project > Build Setting "typecheck calls to printf/scanf : NO"
Explanation : [How it works]
Check calls to printf and scanf, etc., to make sure that the arguments supplied have types appropriate to the format string specified, and that the conversions specified in the format string make sense.
Hope it work
[caution: It may void other warning of 64 Bits architecture conversion].

Working with big numbers in Objective-C?

I need to convert values like 1393443048683555715 to HEX. But, first of all, i cann't display it as decimal using NSLog(), for example.
Ok, it works:
NSLog(#"%qu", 1393443048683555706);
But what about converting to HEX. What type i have to use to store this big value?
NSLog([NSString stringWithFormat: #"%x", 1393443048683555706]);
// result eb854b7a. It's incorrect result!
but i forgot to say that this big number represented as string #"1393443048683555706" (not int)
You can use %qi and %qu format specifiers with NSLog to display 64-bit integers. Your constant appears to fit in 64-bit signed number, with the limits of:
[−9223372036854775808 to 9223372036854775807]
The "x" format specifier is for 32-bit numbers; you need to use either "qx" or "qX" (depending on whether you want the letter values to be uppercase or not). These are the formatters for unsigned long long values, see:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html#//apple_ref/doc/uid/TP40004265-SW1
Next, you should not pass a string as you have done above directly to NSLog - this can cause a crash.
NSLog(string); // bad!!
NSLog(#"%#", string); // good
So if your value comes as a string, you'll want to do this:
NSString *longNumber = #"1393443048683555706";
NSLog(#"%qx", [longNumber longLongValue]);
If the string value can't be coerced to a number, longLongValue will return 0. I'll leave it to you do handle the error (and bounds) checking - see NSString for details.
If you want to save the hex value as a string, do this:
NSString *hexRepresentation = [NSString stringWithFormat:#"%qx", [longNumber longLongValue]];
Again, best to take care for error handling.

Why does only NSLog warn me about using the %lu string format specifier for NSUInteger?

For some reason, I get a compilation error when I try to do the following:
NSLog(#"row: %lu", indexPath.row);
where row is of type NSUInteger. The error I get is
Conversion specifies type 'unsigned long' but the argument has type 'NSUInteger' (aka 'unsigned int')
I can do the following with no compilation errors:
NSString * string = [NSString stringWithFormat:#"row: %lu", indexPath.row];
I'm using the exact same format string and substitution argument in both cases, but why does NSLog freak out while -stringWithFormat: seems to be perfectly content? My compiler is LLVM 1.6.
All devices that iOS currently runs on are 32-bit. If you want to silence the warning:
NSLog(#"row: %lu", (unsigned long)indexPath.row);
[Edit: As of the iPhone 5s, it is no longer true that iOS is always 32-bit.]
I've run into this same issue, and although #Wevah is correct and his answer works just fine, there's another option that doesn't require any code changes. See the following Apple documentation for details:
String Programming Guide | Platform Dependencies
64-Bit Transition Guide for Cocoa | Building 32-Bit Like 64-Bit
The NS_BUILD_32_LIKE_64 preprocessor macro is quite helpful. You can set it in your Xcode project settings (under GCC_PREPROCESSOR_DEFINITIONS) or just put #define NS_BUILD_32_LIKE_64 1 in your precompiled header (.pch) file. In my app, this eliminated 11 warnings without any code changes.
This works because unsigned int and unsigned long are the same size (4 bytes) on iOS, so changing the typedef of NSUInteger makes the compiler (and the developer) happy, but the hardware doesn't care, since it's just doing integer math in both cases. :-)
Apple documentation recommends casting the 64 bit value to a 32 bit value using %lu and %ld. This poses a problem if you actually use the extra 32 bits. Format strings %qu and %qd specify a 64 bit value (unsigned and signed respectively). If you want code that will compile in either mode, then values declared as NSUInteger or NSInteger will have to be cast to a UInt64 or SInt64 in the parameter list to avoid the warning.

NSLog/printf specifier for NSInteger?

A NSInteger is 32 bits on 32-bit platforms, and 64 bits on 64-bit platforms. Is there a NSLog specifier that always matches the size of NSInteger?
Setup
Xcode 3.2.5
llvm 1.6 compiler (this is important; gcc doesn't do this)
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF turned on
That's causing me some grief here:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
#autoreleasepool {
NSInteger i = 0;
NSLog(#"%d", i);
}
return 0;
}
For 32 bit code, I need the %d specifier. But if I use the %d specifier, I get a warning when compiling for 64 bit suggesting I use %ld instead.
If I use %ld to match the 64 bit size, when compiling for 32 bit code I get a warning suggesting I use %d instead.
How do I fix both warnings at once? Is there a specifier I can use that works on either?
This also impacts [NSString stringWithFormat:] and [[NSString alloc] initWithFormat:].
Updated answer:
You can make use of the z and t modifiers to handle NSInteger and NSUInteger without warnings, on all architectures.
You want to use %zd for signed, %tu for unsigned, and %tx for hex.
This information comes courtesy of Greg Parker.
Original answer:
The official recommended approach is to use %ld as your specifier, and to cast the actual argument to a long.
The accepted answer is absolutely reasonable, it is standard conforming, and correct. The only problem is that it doesn't work anymore, which is completely Apple's fault.
The format %zd is the C/C++ standard format for size_t and ssize_t. Like NSInteger and NSUInteger, size_t and ssize_t are 32 bit on a 32 bit system, and 64 bit on a 64 bit system. And that's why printing NSInteger and NSUInteger using %zd worked.
However, NSInteger and NSUInteger are defined as "long" on a 64 bit system, and as "int" on a 32 bit system (which is 64 vs 32 bit). Today, size_t is defined on "long" on all systems, which is the same size as NSInteger (either 64 or 32 bit), but a different type. Either Apple's warnings have changed (so it doesn't allow passing the wrong type to printf, even though it has the right number of bits), or the underlying types for size_t and ssize_t have changed. I don't know which one, but %zd stopped working some time ago. There is no format today that will print NSInteger without warning on both 32 and 64 bit systems.
So the only thing you can do unfortunately: Use %ld, and cast your values from NSInteger to long, or from NSUInteger to unsigned long.
Once you don't build for 32 bit anymore, you can just use %ld, without any cast.
The formatters come from the standard UNIX/POSIX printf function. Use %lu for unsigned long, %ld for long, %lld for long long, and %llu for unsigned long long. Try man printf on the console, but on Mac it is incomplete. The linux manpages are more explicit http://www.manpages.info/linux/sprintf.3.html
Both warnings can only be fixed by NSLog(#"%lu", (unsigned long)arg); combined with a cast as the code will be compiled in 32 AND 64 bit for iOS. Otherwise each compilation creates a separate warning.