i'm new to Xcode objective-c and I have a task to make a newsletter that downloadable.
So, I got some source code and tweak a bit but I got some error that said
"Implicit conversion loses integer precision : 'long' to 'int'
here are my code
-(void)downloadIssue:(IssueInfo*)issueInfo{
NewsstandDownloader* downloader = [[AppDelegate instance] newsstandDownloader];
downloader.delegate = self;
long index = [self.publisher indexOfIssue:issueInfo];
[downloader downloadIssue:issueInfo forIndexTag:index]; <-- Error
}
Please help me.
Thank you.
That's just a compiler warning and a mild one at that. If you were dealing with a document that had more than, say, 32000 pages then you might need to be concerned about it.
The way to solve the problem is to either change the declaration of the function you're calling to something like:
[downloader downloadIssue:(IssueInfo *)issueInfo forIndexTag:(long)index]
or, simply use a cast:
int index = (int)[self.publisher indexOfIssue:issueInfo];
"int" isn't usually a good thing to use in Objective C as there are different lengths and capacities to it on different platforms (32 bit versus 64 bit, iOS vs MacOS, etc.). It's better to use something more Objective-C specific, like NSInteger or NSUInteger.
Related
I made a member struct that I assigned in the ViewDidLoad of my iOS app. I used malloc to allocate space for this struct that was then used throughout my class. Like this:
self.myData = malloc(sizeof(MyData));
Except what I really did was this:
self.myData = malloc(sizeof(MyOtherStruct));
I accidentally set sizeof() in the malloc call to be a different struct (that isn't the same size). I didn't notice this mistake for a very long time because the app only rarely crashed. An update to the OS caused the crash to happen more frequently.
My question is, why can compiler's not warn about this sort of thing? Is it something compiler's don't know about or is it a design choice to allow users's to malloc whatever size they please?
"How can I find this error faster?"
There are a bunch of ways to find the error faster.
Solution #1
The static analyzer cathes this error. Press command-shift-B in Xcode. For example, take the following code:
#include <stdlib.h>
struct x { double x; };
struct y { char y; };
int main(int argc, char **argv) {
struct x *p = malloc(sizeof(struct y));
p->x = 1.0;
return 0;
}
Running the analyzer produces this error for me:
Result of 'malloc' is converted to a pointer of type 'struct x' which is incompatible with sizeof operand type 'struct y'
Solution #2
It is recommended to write the code this way instead:
self.myData = malloc(sizeof(*self.myData));
Just do it this way in the future. This is not only less error-prone, but it is easier to remember.
Solution #3
Use a language like Swift or C++ where the language's type system help you avoid this kind of error. C is less forgiving in many ways. It was invented in the early 1970s, you just kind of have to accept that if you want to use it, and these kinds of errors are a major part of the reason why C++ and Swift even exist in the first place.
Solution #4
Use a run-time memory bounds checker, like the address sanitizer. This will detect the error when memory is accessed, not when it is allocated, but it will still give you stack traces for both access and allocation (and free, if the memory has been freed). Anyone writing C these days should familiarize themselves with the address sanitizer and its friends, tsan, ubsan, etc.
Valgrind also achieves the same effect but the address sanitizer has a better user experience for common use cases.
Question as asked
The compiler only really gives you errors and warnings for type errors. This isn't a type error, it's a runtime error. There are a few "likely" runtime errors that the compiler can detect, but they are very few in number. Things like forgetting to use the return value of malloc()... e.g.,
void f(void) {
malloc(1); // warning
}
The compiler isn't much better than that.
Again, this is the impetus for newer languages like C++ and Swift, which have type systems which allow you to generate errors when you allocate things incorrectly, and this is also the impetus for static analysis (which is a tough problem).
That happens because is not ARC responsability to deal with malloc() and even free()
The ARC just handle with objects allocated like [Object alloc]
In your case, when you do self.myData = malloc(sizeof(MyOtherStruct));, that can be interpreted for example with something like this:
self.myData = malloc(N*sizeof(MyData));
//what can represents self.myData[0]..self.myData[N-1]
For the last, remember when you use sizeof(), it will tell you about the size of the type, that you are passing as a paramter, calculated in compile-time.
You can check this link for more information about object allocation
And also check Apple Documentation about Memory Alloc
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].
I've been developing iOs and OsX applications for several months now and it still feels like I'm doing something wrong. I try to stick to the Guidelines and I try to use the objects Apple provides as often as I can. But it seems they are making my code very hard to understand.
Example:
When I want to just "increment" a NSNumber Object (which is not mutable, but you get what I mean), I use awkward lines like this:
int value = [counter intValue];
counter = [NSNumber numberWithInt:value +1];
Is this really necessary? Are there more elegant ways (i++, inc(i), etc) to do simple things like this? Especially when you're working with coordinates it gets really frustrating and hard to work with.
When working with Objective C I feel like I'm allocating, deallocating and converting objects all the time and wasting so much of my own time and the CPU time with all those conversions. Thanks for your time, I really appreciate your answers and I'm looking forward to your tipps!
Using your example, is there any particular reason you are using NSNumber for a counter? It would be much better to use int so that you can use value++.
The key to good Objective-C code is to use objects when they make sense. Don't be afraid to use non-object data types and don't be afraid to drop down (not the best term) to C when required.
As #sosborn wrote: use objects only when it's required. But: when it's required, and you still feel wrong, simply don't. Write a macro for incrementing an NSNumber, use ARC for let the compiler do the memory management for you as efficiently as possible, etc. If you really worried about time, use C or assembly for time-critical tasks, or C++ if you want OO.
P. s.: NSNumber increment macro:
#define NSNUM_INC(n) do { n = [NSNumber numberWithInt:[n intValue] + 1]; } while (0);
You can write your category for NSNumber to implement the methods you need. For your example the file of category contains the following function:
-(NSNumber *)numberByAddingInt:(int)i
{
...
}
Include this file and then you can call it as:
counter = [counter numberByAddingInt:1];
Occasionally, during development/debugging, I want to ensure that an object is of a certain type:
PageTopBottom *newPage = [notification object];
assert([newPage isKindOfClass:[PageTopBottom class]]);
which I've worked into this
#define assertType(_var_, _class_) assert([_var_ isKindOfClass:[_class_ class]])
and
PageTopBottom *newPage = (id)[notification object];
assertType(newPage, PageTopBottom);
but now I'd like to, if possible, just use
assertType(newPage)
Is it possible to get information about a variable's declared type from the variable?
I'm not positive that I'm framing the question correctly, but any answer that gets me to be able to assertType with one parameter would be great.
Is it possible to get information about a variable's declared type from the variable?
No. By the time the program is running, that information is lost. In your case, newPage is just a 32 or 64 bit number that points to a bit of memory that holds an Objective-C object.
I think your original unmacro'd version is the right thing to do here:
assert([newPage isKindOfClass:[PageTopBottom class]]);
That perfectly documents the assumption you are making i.e. that you assume newPage is an instance of PageTopBottom or one of its subclasses and it's completely clear to anybody who understands Objective-C. Your macro version slightly obfuscates that, in that somebody coming across it in the code might beleive it is asserting that newPage is a PageTopBottom and not one of its subclasses (you could change the name of the macro to prevent that, I suppose, but I just wouldn't bother).
Edit
What you could do is combine the declaration and assertion in one:
#define DECLARE_AND_ASSERT_IS_KIND_OF_CLASS(T, V, I) T* V = (T*)(I); assert([(V) isKindOfClass: [(T) class])
which would work like this:
DECLARE_AND_ASSERT_IS_KIND_OF_CLASS(PageTopBottom, newPage, [notification object]);
Hmm, with Objective-C++ there are two options:
Write a template function
template void assertType(T* obj) { ... }
For a pointer X* x, use NSClassFromString([NSString stringWithUTF8String:typeid(*x).name()]).
Without using C++, you might be able to use GCC extension typeof, but I'm not sure if [typeof(*x) class] is a legit operation...
The preprocessor only processes text; it has no knowledge of type, which is why it's sometimes considered 'dangerous'. The only way I could see doing it is wrapping the variable declarations in a macro, which I would strongly advise against, and probably wouldn't actually cut down on the code or complexity.
Also, shouldn't you check the type before casting?
Total newbie question but this is driving me mad!
I'm trying this:
myInt = [myFloat integerValue];
but I get an error saying essentially integerValue doesn't work on floats.
How do I do it?
I'm pretty sure C-style casting syntax works in Objective C, so try that, too:
int myInt = (int) myFloat;
It might silence a compiler warning, at least.
what's wrong with:
int myInt = myFloat;
bear in mind this'll use the default rounding rule, which is towards zero (i.e. -3.9f becomes -3)
int myInt = (int) myFloat;
Worked fine for me.
int myInt = [[NSNumber numberWithFloat:myFloat] intValue];
Well, that is one option. If you like the detour, I could think of some using NSString.
Why easy, when there is a complicated alternative? :)
You can also use C's lroundf(myFloat).
An incredibly useful tip: In Xcode's editor, type your code as say
myInt = roundf(someFloat);
then control/right-click on roundf and Jump to definition (or simply command-click).
You will then clearly see the very long list of the functions available to you. (It's impossible to remember them all, so just use this trick.)
For example, in the example at hand it's likely that lrintf is what you want.
A further tip: to get documentation on those many functions. In your Terminal.app (or any shell - nothing to do with Xcode, just the normal Terminal.app) simply type man lrintf and it will give you full info. Hope it helps someone.
In support of unwind, remember that Objective-C is a superset of C, rather than a completely new language.
Anything you can do in regular old ANSI C can be done in Objective-C.
Here's a more terse approach that was introduced in 2012:
myInt = #(myFloat).intValue;