I'm getting the subject warning on the following line:
NSSortDescriptor * sort = [ [NSSortDescriptor alloc] initWithKey:#"name"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:) ];
And I didn't always get that warning. That is, the warning just started to appear recently, possibly due to a change in compilation settings. I don't see a typo, here. This looks (to me) just like code I've seen in many examples. I'm not seeing a run time exception. But I don't like warnings. Anyone seen this? Any idea what's triggering it?
Did you turn on -Wselector? (Also called "Multiple Definition Types for Selector.") This warning is incompatible with Foundation and you should not turn it on. It exists because it does make sense in "pure" Objective-C. Just not in any Objective-C you'd ever be likely to write (i.e. anything that uses Cocoa).
The warning you want is -Wundeclared-selector, also called "Undeclared Selector."
It's somewhat dated, but I've compiled a complete list of GCC warnings and whether to turn them on or off in my Shared.xcconfig file. There is also a very helpful bestiary compiled by Jean-David Gadina that is a bit more up-to-date.
Related
I am (re)building a project written in objective c in XCode 3.2.6 64 bit under a 10.6.8 VM based on the 10.5 SDK (for compatibility with 10.5 and up - so this is a given.) The build is up and working. But. I'm trying to address the last four warnings in this project. They're all the same warning. Specifically...
In the project, there are four ASCII c strings that need to be converted to four corresponding instances of objective c's NSString. There are four essentially identical cases. This is how it's being done:
[tf setStringValue: [NSString stringWithCString: strg]]
This works, but results in (four) warnings that stringWithCString is deprecated and looking further, I find that's been true since about 10.4. So I'd expect the 10.5 SDK to have whatever replacement is required.
Looking at the docs, the suggested replacement is:
[tf setStringValue: [NSString stringWithCString:NSASCIIStringEncoding: strg]]
However, when this is used, XCode says:
'NSString' may not respond to '+stringWithCString::'
Which probably means it really won't respond. And besides, even if it does, replacing one warning with another.... yech.
Anyone know what I should be doing differently? I realize that this is old, old stuff, but surely Back In The Day people didn't just let these warnings clutter up their builds? Have I just got some kind of syntax error here, or... ?
[NSString stringWithCString:NSASCIIStringEncoding: strg]
The correct syntax for calling this method is:
[NSString stringWithCString:strg encoding:NSASCIIStringEncoding]
I'm adding code provided by a partner to my iOS project that calls class_createInstance and then calls autorelease before returning, like this:
Class functionClass = objc_getClass(functionName);
NSObject* functionObject = class_createInstance(functionClass, 0);
[[functionObject performSelector:#selector(initWithDictionary:) withObject:msg] autorelease];
When running Analyze in Xcode 4.0.2, I get the following warning on the last line:
Object sent -autorelease too many times
Question 1: How is that object getting sent autorelease too many times?
My understanding of class_createInstance is that I need to release the value it returns.
Question 2: If the code is correct, how can I avoid the warning from Analyze?
We have a pretty strict policy to not check in any Analyze warnings.
This is a decidedly odd pattern and, thus, the analyzer simply isn't aware of it. Use of class_createInstance() is extremely rare.
Two possible solutions off the top of my head:
Use the preprocessor markup to tell the analyzer that, yes, in fact, functionObject is a retained reference (I don't have the markup handy -- you'll find it in the release notes or on the llvm.org site or search the system headers).
Don't use class_createInstance(). Once you have a reference to the class, just use +alloc. Better yet, rewrite the entire expression as [[objc_getClass(functionName) alloc] initWithDictionary:msg] and be done with it.
You should also file a bug via http://bugreporter.apple.com as, though odd, the static analyzer shouldn't barf on that.
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.
I'm supporting 10.4+ by picking the most-current API at runtime:
if ([fileManager respondsToSelector:#selector(removeItemAtPath:error:)])
[fileManager removeItemAtPath:downloadDir error:NULL];
else
[fileManager removeFileAtPath:downloadDir handler:nil];
In this case, 10.5 and up will use removeItemAtPath:error: and 10.4 will use removeFileAtPath:handler:. Great, but I still get compiler warnings for the old methods:
warning: 'removeFileAtPath:handler:' is deprecated [-Wdeprecated-declarations]
Is there a syntax of if([… respondsToSelector:#selector(…)]){ … } else { … } that hints the compiler (Clang) to not warn on that line?
If not, is there a way to tag that line to be ignored for -Wdeprecated-declarations?
After seeing some of the answers, let me clarify that confusing the compiler into not knowing what I'm doing is not a valid solution.
I found an example in the Clang Compiler User's Manual that lets me ignore the warning:
if ([fileManager respondsToSelector:#selector(removeItemAtPath:error:)]) {
[fileManager removeItemAtPath:downloadDir error:NULL];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[fileManager removeFileAtPath:downloadDir handler:nil];
#pragma clang diagnostic pop
}
You could declare a separate file that is designated for calling deprecated methods and set the per-file compiler flags in Xcode to ignore -Wdeprecated-declarations. You can then define a dummy function in that file to call the deprecated methods, and thereby avoid the warnings in your real source files.
I'm not sure if clang is smart enough to catch this, but if it's not, you could try using performSelector:withObject:withObject: or building and invoking an NSInvocation object.
You could just cast fileManager to an id — ids are able to refer to any Objective-C object, so the compiler isn't supposed to check methods which are called on one:
[(id)fileManager removeItemAtPath:downloadDir error:NULL];
shouldn't raise any warnings or errors.
Of course, this raises other problems — namely, you lose all compile-time checking for methods called on the id. So if you misspell you method name, etc, it wont be caught until that line of code is executed.
If you consider any form of "confusing" the compiler to be an invalid solution, you're probably going to have to live with the warning. (In my book, if you asking how to get rid of a warning, it's unwise to look a gift horse in the mouth and say something is invalid just because it doesn't look like you'd expect.)
The answers that work at runtime involve masking the operation that's happening with dynamic dispatch so the compiler doesn't complain about the deprecated call. If you don't like that approach, you can turn off "Warn About Deprecated Functions" in your Xcode project or target settings, but that's generally a bad idea. You want to know about deprecated APIs, but in this case you want to use it without warning. There are easy and hard ways to do this, and odds are you'd consider all of them "invalid" in some form, but that doesn't prevent them from being effective, even correct. ;-)
One possible way to avoid the warnings yet still select at runtime is to use objc_msgSend() directly:
objc_msgSend(fileManager, #selector(removeFileAtPath:error:), downloadDir, nil];
This is what the Objective-C runtime does under the covers anyway, and should accomplish the result you want with a minimum of fuss. You can even leave the original line commented above it for clarity. I know the documentation says, "The compiler generates calls to the messaging function. You should never call it directly in the code you write." You alone have to decide when it's okay to bend the rules.
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