I have a fairly large app (objective-c), I decided to turn on the "Sign Comparison" warnings. I now have almost 100 of them. Most of them are similar to this:
if (recentSearches.count > indexPath.row) {
//something
}
and the errors are similar to this:
Comparison of integers of different signs: 'NSUInteger' (aka 'unsigned int') and 'NSInteger' (aka 'int')
what are some good strategies or tips on how to tackle them all?
Thanks
Really, you want to go ahead and change the declarations so the types match when you compare. For example, a count variable should be unsigned. That's by far the safest. If you are recycling a variable so it's compared to other things that may be both signed or unsigned, change that!
Only typecast when you have explicitly verified it's safe do to so. For example, if a signed int is > 0 you can cast it to unsigned int. The other direction is not safe unless you handle the range of values an unsigned int can hold that are too big for a signed int!
Related
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].
Over at In Cocoa do you prefer NSInteger or just regular int, and why?, there was mention of NSDouble and NSFloat, but I can't see a reference for those in any documentation. If NSInteger's purpose is for architectural safety, what about other types such as double or float?
NSInteger exists because the int type varies in size between 32-bit and 64-bit systems. float and double don't vary in size the same way, so there's no need to have wrapper types for them.
There is no NSFloat but I know the Core Graphics API eventually changed from float to CGFloat so that it could use a double on some architectures.
It is best to use the exact types that API headers declare. This makes type changes automatic if you ever recompile your code for a different target.
It's also about conventions.
A typedef to an int is incompatible to int int itself.
Example: pid_t is of type int, but passing an int would create a warning.
Why? Because you want to be sure that if you cross API boundaries everyone knows what the code expects.
There are float and double types, i.e NSTimeInterval. It's not really about the underlying type, but the convention to adhere to.
If you declare a local int as a loop counter and you do not plan to pass it to a well-defined API, it's fine to call an int an int.
What happens when casting between these two in relation to the termination character? In C99 Objective-C.
I assume that a char is 8 bits on your system in this answer.
If your architecture uses unsigned char as char type then absolutely nothing will happen.
If your architecture uses signed char as char type then negative values of char will wrap around causing possibly unexpected results. This however will never happen to the termination null character.
Please note, by "casting" nothing really happens, you just tell the compiler to interpret a certain location in the memory differently. This difference in interpretation would create the actual (side)effects of the cast.
If char and uint8_t are compatible types (they should be on most current desktop computers), pointers to objects of that type have the same representation and alignment requirements, so there should be no problem converting (implicitly, or explicitly with a cast) one to the other.
The values pointed to, again, if they are compatible, should be treated equally no matter what type they are interpreted as.
Note: I am not 100% certain that uint8_t and char are compatible on a implementation with signed chars.
If the types are not compatible you invoke Undefined Behaviour and anything can happen: a very likely thing to happen is that everything "works" as you expect -- but there is no guarantee it will always work the same
I've recently encountered a very rarely reproducible bug. If I cast a pointer that isn't nil to BOOL for some reason I get NO!!!11 I know that BOOL is typedef of signed char. Can that be a problem?
If the pointer is of the form 0xNNNNNN00 or (on 64-bit) 0xNNNNNNNNNNNNNN00, where N is any hex digit, then a cast to BOOL (an unsigned char) will leave you with 0 since it will truncate all the non-zero bits.
To properly convert a pointer to BOOL, use the inequality operator:
BOOL isNotNull = (myPointer != NULL);
Its because a pointer is 32 bit large and a char is just 8 bits. So when converting from a pointer to a char, it will trim 24 bits. So the possibility that the remaining 8 bits are just zeros is pretty high especially when the pointers address is high.
What you can do is the following:
BOOL myBool = (myPointer != NULL); // This will evaluate if the pointer is true (>0) or false (NULL)
BOOL is not a type that should be used, for this exact reason. In fact I can think of little use for a boolean-type variable (as opposed to just using the original value, pointer or whatever, on which it's based) except in the case of giant bit arrays where you'd need special code to address bits. If you really want a boolean type, use C99 _Bool (or bool in stdbool.h) which has the correct conversion semantics rather than modulo-256 reduction.
I want to know if someVariable is of type int or float. How would I do the check?
Edit: In code at runtime! In the IDE that would be simple. I want to know if there is some kind of typeof operator to see if you're dealing with int, float, long, char, unsigned char, long long or whatever.
if ((1?1:var)/2) {
/* it's floating point */
} else {
/* it's an integer */
}
Is someVariable an NSNumber? If it's a primitive C type (an actual int or float), then you'll already know because it can be declared as only one of those things, and the compiler (and Xcode) will enforce that. As another commenter noted, Command-Double-Click the name to find the declaration in Xcode.
Since sizeof(float) == sizeof(int) (depending on platform, but on i386 and current iPhone this is valid) - i believe you can't decide during runtime whether your variable is int or float.
EDIT: Greg is right in the comment. This check doesn't really make sense.