Do warnings matter? - objective-c

EDIT: I have fixed all but two warnings now, so thank you all for the advice and the encouragement. The two warnings I have left would require me to change the database:
/Locations.xcdatamodel:tiles.Map: warning: tiles.Map -- relationship does not have an inverse
/Locations.xcdatamodel:Waypoint.description: warning: Waypoint.description -- property name conflicts with a method already on NSObject or NSManagedObject
I have an iPhone app that throws more than 100 warnings when I compile it, but it is time-tested and solid.
Should I care about warnings?
EDIT Because the respondents asked, here are some of my warnings:
Warning: Multiple build commands for output file /Users/andrewljohnson/Desktop/thetrailbehind/TrailTracker/build/Debug-iphonesimulator/Gaia Places.app/wrench.png
/Locations.xcdatamodel:tiles.Map: warning: tiles.Map -- relationship does not have an inverse
/Locations.xcdatamodel:Waypoint.description: warning: Waypoint.description -- property name conflicts with a method already on NSObject or NSManagedObject
/TrailTrackerAppDelegate.m:58: warning: passing argument 1 of 'initWithViewController:withTitle:' from distinct Objective-C type
/Users/andrewljohnson/Desktop/thetrailbehind/TrailTracker/Classes/TrailTrackerAppDelegate.m: In function '-[TrailTrackerAppDelegate applicationDidFinishLaunching:]':
/Users/andrewljohnson/Desktop/thetrailbehind/TrailTracker/Classes/TrailTrackerAppDelegate.m:202: warning: no '-initWithFrame:forHelpScreen:' method found
/Users/andrewljohnson/Desktop/thetrailbehind/TrailTracker/Classes/TrailTrackerAppDelegate.m:202: warning: (Messages without a matching method signature
/TrailTrackerAppDelegate.m:329: warning: 'gpsController' may not respond to '-setAccuracy:'
/Users/andrewljohnson/Desktop/thetrailbehind/TrailTracker/Classes
/TrailTrackerAppDelegate.m:411: warning: local declaration of 'tabBarController' hides instance variable
/TrailTrackerAppDelegate.m:422: warning: 'TrailTrackerAppDelegate' may not respond to '-getAudioPlayer:'
/Users/andrewljohnson/Desktop/thetrailbehind/TrailTracker/Classes/TrailTrackerAppDelegate.m:633: warning: 'Reachability' may not respond to '-isHostReachable:'
/Users/andrewljohnson/Desktop/thetrailbehind/TrailTracker/Classes/TrailTrackerMapView.h:18: warning: 'myTopoMapSource' defined but not used
warning: 'dbCache' defined but not used
/TrailTrackerAppDelegate.m:58: warning: passing argument 1 of 'initWithViewController:withTitle:' from distinct Objective-C type
/Users/andrewljohnson/Desktop/thetrailbehind/TrailTracker/Classes
/Users/andrewljohnson/Desktop/thetrailbehind/TrailTracker/Classes/TripViewController.m:68: warning: 'TripViewController' may not respond to '-checkForNullImages'
/Users/andrewljohnson/Desktop/thetrailbehind/TrailTracker/Classes/TripViewController.m:94: warning: 'TrailTrackerAppDelegate' may not respond to '-blamblamblam'
/Users/andrewljohnson/Desktop/thetrailbehind/TrailTracker/Classes/MapViewController.m:406: warning: passing argument 1 of 'initWithData:' from distinct Objective-C type

Yes. Some Warnings can be important.
It's best practice to turn a compiler's warning level to the highest setting, and to try to eliminate all warnings.
If a warning cannot be removed through refactoring code, and it has been checked and deemed safe, then many languages have 'pragmas' to silence the warning.
Update circa 2014: It is increasingly common to turn on a complier option that treats all warnings as errors. I personally do this.

My gut answer would be absolutely, wholeheartedly yes.
Compiler warnings are there to help you write clear and maintainable code, reducing the likelihood of bugs being introduced later on or cropping up at runtime.
The compiler will generally catch things like unused variables, access to uninitialised variables, failure to return correctly from a function, etc. These things may not be an issue now or in testing, but could easily come along and screw you later.
I would be going through those warnings and trying to fix them now. It will be time well spent.

Many of those warnings look like extraneous stuff in your program that's harmless. However, when you have 100 warnings you know don't matter (I'm not saying all of those don't matter, I'm just illustrating) and warning 101 that's very real shows up--the odds are you aren't going to see it.
I do not tolerate any warnings whatsoever. Occasionally this means adding a useless line or two of code because the compiler can't see that a conditional must execute or the like. (A case in front of me as I write this: 4 paths in a switch on a random number. 1 of the 4 must execute and each assigns a value to a variable. The compiler doesn't know it must have a value at that point so I added an extraneous assignment to shut it up.)

lol I guess it depends what the warnings are!!
If you have over 100 and yet it runs just fine, I'd guess that they are probably warnings of the type "Object xxx may not respond to message yyy" - i.e. the new methods you've created within your app have not been set up with an appropriate declaration in the header so your compiler isn't able to check whether they are valid method calls for (or more accurately, messages to send to) your custom classes.
Many warnings in objective-C actually warn you of an error that will crash the application - in fact, there is even an option within XCode to "treat all warnings as errors". The fact that your app runs shows that you are not suffering from this problem (or at least, have not yet).
However, even if all your warnings are benign (which I doubt), there is good reason for fixing them. If its regarding class methods, you'll be able to find out before you experience a crash whether or not your sending a valid message to it. And for most of the other types of warnings, you're better off knowing about it.
I guess the final exception is if you're using a lot of deprecated methods. Then you might decide to leave them be, although again that's a risky strategy.
So we're back to the beginning point - it depends what they are! However, I'm 99% sure that it will be a worthwhile exercise fixing them. They're there to help you, not it!

Definitely Yes. Otherwise why would it be called a Warning?

If I could give ObjC developers one piece of advice, it would be "use accessors, always." But if I could give them two pieces of advice, the second piece would be "turn on 'treat warnings as errors'."
Maintaining a zero warning policy in Cocoa is perhaps the best cost/benefit trade-off I know of. There are very few warnings that cannot be fixed easily in Cocoa. When writing portable C++, it is often very challenging (sometimes nearly impossible) to avoid warnings, but Cocoa has a single compiler on two very similar platforms. There's almost nothing you need to be doing in Cocoa that should be tripping the default warning set.
Warnings breed warnings. When you say "well, I know about those 121 warnings and they're ok," it's very easy to miss when it becomes 122 warnings and that new one does matter. If you have a warning that you really need to work around, then suppress the warning, but don't ignore them in the build output.
My team turns up warnings very high, and over time we've had to tune them back down a little bit, but we still build major Objective-C++ projects on Mac and iPhone under the following warning flags. There are a few of them that could certainly come out for a particular project, but this is my team's default set, and we don't take many out. I talk about it a little more in my discussion of project templates. The zip file there includes Shared.xcconfig which documents what each of these does and why it's turned on or off. But the default set that Xcode gives is a minimum.
GCC_TREAT_WARNINGS_AS_ERRORS = YES
WARNING_CFLAGS = -Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Wno-unknown-pragmas -Wextra-tokens -Wformat-nonliteral -Wformat-security -Winit-self -Wswitch-enum -Wswitch-default -Wfloat-equal -Wpointer-arith -Wredundant-decls -Winvalid-pch -Wlong-long -Wdisabled-optimization
OTHER_CFLAGS = -Wnested-externs -Wold-style-definition -Wstrict-prototypes -Wundeclared-selector
OTHER_CPLUSPLUSFLAGS = -Wsign-promo -Wundeclared-selector
GCC_WARN_CHECK_SWITCH_STATEMENTS = YES
GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES
GCC_WARN_64_TO_32_BIT_CONVERSION = YES
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES
GCC_WARN_ABOUT_RETURN_TYPE = YES
GCC_WARN_MISSING_PARENTHESES = YES
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES
GCC_WARN_SIGN_COMPARE = YES
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES
GCC_WARN_UNUSED_FUNCTION = YES
GCC_WARN_UNUSED_LABEL = YES
GCC_WARN_UNUSED_VALUE = YES
GCC_WARN_UNUSED_VARIABLE = YES
GCC_WARN_UNINITIALIZED_AUTOS = YES

Absolutely. You should always resolve your warnings. If you can't, you should at least know why the warning is there and why you can ignore it. To just ignore all compiler messages, (however innocuous as they may seem), is just a recipe for disaster.

YES

Related

Should i cast with NSInteger, NSNumber, and NSUinteger?

In objective c, I often run into warnings in the compiler when I have methods that (for instance) return/take as an argument an NSInteger, and I instead place the argument as say a long int/NSNumber/etc value or variable. The code compiles fine, but I am always tempted to just do casts because the warnings make me uneasy. I understand that it probably does not make a big difference either way, but is casting the preferred way to handle these warnings or not?
int, NSInteger, and NSUInteger are scalars. NSNumber is an object. They have nothing to do with one another, and casting between them to hide that fact from the compiler will bring disaster.
Remember, casting means that you throw away the compiler's ability to check that you are doing the right thing. You can use casting to lie to the compiler, as if to say to it, "Don't worry, everything will be okay." Do not lie to the compiler! If you do, then when the app runs, you will get nonsense at worst, and a crash at best. (The crash is best, because it stops you dead; not crashing means you now have a big mistake that will be very difficult to track down later.)
If the warning is just a cast issue then by all means add the cast. Many times Xcode is very helpful with this.
Many times the best answer is to change the declared type so there is neither an error nor a warning. Example: You have declared a variable as an int. A method is expecting a NSInteger. Instead of a cast change the declaration of the variable to an NSInteger.
But note that casting an NSNumber to an NSInteger for example will not fix anything, as #Josh points out they are very different things.
Eliminate all warnings! Then when a new one occurs it is readily apparent. If there are warnings then the code is not compiling fine.
Note: There are times when individual warnings will need to be eliminated with a #pragma but these are extremely rare and only done when the cause is completely understood. An example: I needed to be able to cause a crash in an application for testing, that code caused a warning, a #pragma was added to eliminated the warning.
Also run Analyzer and fix any warnings there.
The warnings are there for a reason, they help catch errors.

convert SAFELY 64 bit app into a 32/64bit app

i made an app (my first one) with ARC enabled, distributed (not over the appstore) and started getting reports that it crashed on some macs. after trying and searaching with theyr help we found out that the problem is that they have a 32 bit processor. so i disabled ARC, set the build to x86_64 and got a load of error messages.
all of them have been sorted, and now i am left with a load of warnings (yellow ones). i am worried that i forget something and make a mess with the memory. what is in your opinion the best way to get froma 64 bit app to a 32/64 bit app without forgetting something?
why is ARC used in the first place if there are many macs that aren't compatible with it?
thanks!
-----UPDATE ------
as reuquested:
Update to recommended settings
Property 'delegate' requires method 'delegate' to be defined - use #synthesize, #dynamic or provide a method implementation in this class implementation
Property 'delegate' requires method 'setDelegate:' to be defined - use #synthesize, #dynamic or provide a method implementation in this class implementation
'__bridge' casts have no effect when not using ARC
Variable 'loc_tip' is used uninitialized whenever 'if' condition is false
Variable 'loc_tip' is used uninitialized whenever '&&' condition is false
Conflicting parameter types in implementation of 'tableView:toolTipForCell:rect:tableColumn:row:mouseLocation:': 'NSInteger' (aka 'int') vs 'long'
Rough Water Ahead
all of them have been sorted, and now i am left with a load of warnings (yellow ones).
turn up the warning level. fix them all.
i am worried that i forget something and make a mess with the memory.
what is in your opinion the best way to get froma 64 bit app to a 32/64 bit app without forgetting something?
worry is natural, considering the problem.
one quick fix would be to use ObjC with Garbage Collection enabled. doing so and trusting it will just work with no extra changes is a mistake (read: you should reserve a lot of time for testing and bug hunting).
the other approach, if you really want good support on both 32 and 64, would be to use MRC -- but you should just commit to MRC for both 32 and 64 bit in that case (no ARC). of course, this will require a lot of reviewing, fixing, manual testing, and testing for leaks (unless your program is small). this is the preferable solution for an app with good long term support and high quality standards, IMO.
why is ARC used in the first place if there are many macs that aren't compatible with it?
there really aren't a whole lot of macs out there that require 32 bit. if you were releasing a 1.0 today, you should just consider making it 64 bit only.
32 bit should have been supported and tested from the beginning of development, if it was a requirement -- either the ARC option or 32 bit would have been eliminated very early on. it seems odd that this oversight would slip through testing and development.
Update to recommended settings
Hit "Validate Settings", review, perform changes.
Property 'delegate' requires method 'delegate' to be defined - use #synthesize, #dynamic or provide a method implementation in this class implementation
echo warning message
Property 'delegate' requires method 'setDelegate:' to be defined - use #synthesize, #dynamic or provide a method implementation in this class implementation
echo warning message
'__bridge' casts have no effect when not using ARC
you would just use a c-style cast in that case
Variable 'loc_tip' is used uninitialized whenever 'if' condition is false
ARC does this for you. just do what ARC would do:
NSSomething * loc_tip = nil;
Variable 'loc_tip' is used uninitialized whenever '&&' condition is false
echo previous response
Conflicting parameter types in implementation of 'tableView:toolTipForCell:rect:tableColumn:row:mouseLocation:': 'NSInteger' (aka 'int') vs 'long'
the selector's declaration does not match -- copy the declaration from the header (NSTableView.h) and see if that change (of parameter types) requires any other changes to your implementations.

Garbage value undetected in debug mode

I've recently discovered the following in my code:
for (NSInteger i; i<x; i++){
...
}
Now, clearly, i should have been initialised. What I find strange is that while in "debug" profile (XCode), this error goes undetected and the for loop executes without issue. When the application is released using the "release" profile, a crash occurs.
What flags are responsible for letting this kind of mistake execute in "debug" profile?
Thanks in advance.
This could be considered a Heisenbug. A declaration without an initialization will typically allocate some space in the stack frame for the variable and if you read the variable you will get whatever happened to be at that location in memory. When compiled for the debug profile the storage for variables can shift around compared to release. It just happens that whatever is in that location in memory for debug mode does not cause a crash (probably a positive number) but when in release mode it is some value that causes a crash (probably a negative number).
The clang static analyser should detect this. I have the analyse when building option switched on always.
In the C language, using an initialized variable isn't an error but an Undefined Behavior.
Undefined behavior exists because C is designed to be a very efficient low-level language. Using an initialized variable is undefined behavior because it allows the compiler to optimize the variable allocation, as no default value is required.
But the compiler is licensed to do whatever he wants when an undefined behavior occurs. The C Standard FAQ says:
Anything at all can happen; the Standard imposes no requirements. The program may fail to compile, or it may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended.
So any implementation of an undefined behavior is valid (even if it produces code that formats your hard drive).
Xcode uses different optizations for Debug and Release configurations. Debug configuration has no optimization (-O0 flag) so the compiled executable must stays close to your code, allowing you to debug it more easily. On the other hand, Release configuration produces strongly optimized executables (-Os flag) because you want your application to run fast.
Due to that difference, undefined behaviours may (or may not) produce different results in Release and Debug configurations.
Though the LLVM compiler is quite verbose, it does not emit warnings by default for undefined behaviors. You may however run the static analyzer, which can detect that kind of issues.
More information about undefined behaviors and how they are handled by compilers in What Every Programmer Should Know About Undefined Behavior.
I doubt it is so much flags as the compiler is optimizing out the "unused" variable i. Release mode includes far more optimizations then debug mode.
Different compiler optimizations may or may not use a different memory location or register for you uninitialized variable. Different garbage (perhaps from previously used variables, computations or addresses used by your app) will be left in these different locations before you start using the variable.
The "responsibility" goes to not initializing the variable, as what garbage is left in what locations may not be visible to the compiler, especially in debug mode with most optimatizations off (e.g. you got "lucky" with the debug build).
i has not been initialized . You are just declaring the i variable not initializing the variable.
Writing just NSInteger i; just declares a variable not initializes it.
You can initialize the variable by below mentioned code.
for (NSInteger i=1; i<x; i++){
...
}

Can you show compiler warnings for error:nil?

Is there a compiler setting that can warn about these? Currently I'm reviewing code and putting in #warning don't use error:nil whenever I see them.
(I know it's sometimes appropriate to do, but maybe there is a better way to have the compiler check sloppy error handling?)
No, since error:nil is completely legal and totally legit (as you even say), there is no way for the compiler to check it.

Objective-C: Assertion vs. Exception vs. Error

In Cocoa, when should I use NSAssert, NSException, NSError?
Here's what I've been thinking:
NSAssert - When creating any client program used for the programmers own benefit to double check rules, conventions, assumptions, or pre-conditions and post-conditions?
NSException - When creating a third-party library for the benefit of other programmers that use the library, so that they immediately know when an input is invalid?
NSError - When interfacing with an external system to get data like a file, database, or web service that isn't guaranteed to give me a result?
An NSAssert will throw an exception when it fails. So NSAssert is there to be short and easy way to write and to check any assumptions you have made in your code. It is not (in my opinion) an alternative to exceptions, just a shortcut. If an assertion fails then something has gone terribly wrong in your code and the program should not continue.
One thing to note is that NSAssert will not be compiled into your code in a release build, so this is typically used for sanity checking during development. I actually tend to use a custom assert macro that is always active.
The times you would #throw your own NSException are when you definitely want it in a release build, and in things like public libraries/interface when some arguments are invalid or you have been called incorrectly. Note that it isn't really standard practice to #catch an exception and continue running your application. If you try this with some of Apple's standard libraries (for example Core Data) bad things can happen. Similar to an assert, if an exception is thrown the app should generally terminate fairly quickly because it means there is a programming error somewhere.
NSErrors should be used in your libraries/interfaces for errors that are not programming errors, and that can be recovered from. You can provide information/error codes to the caller and they can handle the error cleanly, alert the user if appropriate, and continue execution. This would typically be for things like a File-not-found error or some other non-fatal error.
The convention in Cocoa is that an exception indicates a programmer error. A lot of code, including framework code, is not designed to work properly after an exception is thrown.
Any sort of error that should be recoverable is represented by an NSError. There’s also a system for presenting NSErrors to the user. As you say, this is mostly useful for fallible external resources.
Conceptually, an assertion is a statement that a given predicate always evaluates to true; if it doesn’t, the program is broken. While its behaviour can be modified, the NSAssert family is by default a convenient way of throwing NSInternalInconsistencyExceptions (with the option of turning them off in release builds).
Edit:
In Xcode 4.2, assertions are turned off by default for release builds,
Now NSAssert will not be compiled into your code in a release build, but you can change it in build settings
#Mike Weller, There are one wrong in your answer.
One thing to note is that NSAssert will not be compiled into your code in a release build, so this is typically used for sanity checking during development.
Actually, NSAssert will be compiled into your code if you don't add NS_BLOCK_ASSERTIONS in your precompiled prefix files.
In Technical Note TN2190 we can find:
Macros like NDEBUG to turn off C assert or NS_BLOCK_ASSERTIONS to turn off Foundation's NSAssert are important to specify for your precompiled prefix files
Or you can read this one:How to know if NSAssert is disabled in release builds?
In general, exceptions are used to signal programmer errors — they're things that shouldn't happen. Errors are used to signal error conditions that might come up in the normal operation of the program — user errors, basically, or external conditions that need to be true but might not be. So trying to delete some locked element in a document might be an error, and trying to download a file without an Internet connection would be an error, but trying to access an invalid element in a collection would be an exception.
Assertions are typically used in testing, and AFAIK are not used as a general error-handling mechanism like the others.