In my application I have the following Objective-C code:
-(void)layoutPages
{
NSMutableArray* sections = [NSMutableArray array];
[sections addObject:[[NSAttributedString alloc] initWithString:#"Hello world"]];
for (NSAttributedString* contentSection in sections) {
NSLog(#"%#",contentSection);
}
}
Console output: 2014-04-22 14:11:01.505 MyApp[24784:830b] Hello world{}
If I compile for x86_64 architecture using -Os optimization, LLVM then silently optimizes out the loop variable 'contentSection'. When I use -O0, the bug disappears.
This is the output when I try to print the description of the contentSection variable:
(lldb) po contentSection
error: Couldn't materialize struct: the variable 'contentSection' has no location, it may have been optimized out
Errored out in Execute, couldn't PrepareToExecuteJITExpression
How is that possible? From my point of view a loop variable should never be optimized out when used inside the loop. I have seen that other people have a similar issue with LLVM but not with a loop variable. Could this be a compiler bug?
This is probably a compiler settings issue. First you'll want to check that your run scheme is not in release mode. Go to "Edit scheme..." -> "Run" -> "Info" -> "Build Configuration". Make sure the value is set to "Debug".
If that is not the problem, then make sure your debug build settings don't have compiler optimization turned on. Make sure "Optimization Level" is set to "none" for debug. Also make sure there is no other place where the compiler optimization levels might be set, such as in the "Other C Flags" setting.
Related
I found something very confusing about non-exists extern variable.
As the code below, obviously, compiler will generate an error cause there is not a int variable named iiii.
But, if the line code of "NSLog(#"%p", &iiii);" is deleted, no error will be generated. And the "not null" will be printed if I execute the output binary.
Why is this happen? I am hoping to get an error, no matter how I manipulate iiii.
Thanks for your help
IDE: Xcode 10.3 with clang
Target platform: iOS simulator 12.4
extern int iiii;
if(NULL != (&iiii)) {
NSLog(#"not null");
}
NSLog(#"%p", &iiii);
Building an application includes the distinct stages of compilation and linking.
It is the linking stage, which occurs after compilation, that global declarations and "extern" references to such declarations and linked together. If you look at the error you get when the NSLog() is in your code you will see it is a linker (coming from ld, the linker/loader) error.
During compilation the compiler assumes that any extern references will be resolved by the linker. So it assumes the iiii variable will exist, and as such it will have an address so it can optimise away the test.
The above assumes strong linking, when a compiler is informed that weak linking will be used then it cannot make the assumption that an external reference will be non-null. E.g. if the variable iiii was declared in a library/framework which was weakly dynamically linked to your application then the compiler could not assume the variable existed and the application would need to test somewhere that the weakly-linked dynamic library was present. However we're getting into another topic entirely... (ask a new question if you have a dynamic linking issue).
HTH
NSArray rather dislikes being passed a nil object as part of its constructor:
UIView *aView;
UIView *aSecondView = [[UIView alloc] init];
NSArray *array = #[aView, aSecondView];
will throw an exception at runtime when array is created.
Does clang have any facilities to try and detect this kind of error? For some trivial cases (like the one above: a stack-local variable that's never assigned to), it seems like the sort of problem the static analyzer would hit out of the park.
TL;DR: -Wuninitialized catches that specific example, __attribute__((nonnull)) for parameters to functions/methods would help catch nil as argument in general.
For this specific example (variable not specifically initialized), you can use -Wuninitialized to catch those uses:
○ xcrun clang a.m -F/System/Library/Frameworks -c -o a.o -Wuninitialized
a.m:6:22: warning: variable 'aView' is uninitialized when used here [-Wuninitialized]
NSArray *array = #[aView, aSecondView];
^~~~~
a.m:4:16: note: initialize the variable 'aView' to silence this warning
NSView *aView;// = nil;
^
= nil
1 warning generated.
For passing nullptr/NULL/nil to functions/methods, __attribute__((nonnull)) should work for most cases. Since this is an API provided by Apple, you would need to file a radar and hope they add it.
P.S: Actually, __attribute__((nonnull)) wouldn't “simply work”, in this case (if you initialized aView to nil, for example. The #[...] sequence seems to be creating an array, and calling +[NSArray arrayWithObjects: count:], where you would be able to mark the first argument as nonnull, but not the things pointed to by it. In any case, it should be relatively simple to write a clang pass for the analysis you mention. You should file a radar, since such a pass could avoid lots of time losses.
P.P.S: It seems __attribute__((nonnull)) doesn't propagate much information, which is sad. :-( If you have a function f(type arg __attribute__((nonnull))) and you pass it a variable which you initialized to nil and never touched again, it doesn't warn.
I believe you are right, Clang static analyzer hit this error out of the park. It is built into Xcode (6 Beta as I tested on). Go Building Settings > Static Analyzer - Analysis Policy > Set Analyze During 'Build' to Yes. To manually perform, go Product > Analyze.
Static analyzer can also be used as stand alone tool in command line. Other option to analyze at compile time is to add Run Script in Build Phases before Compile Sources.
Is there a way I can get a warning when I'm assigning a variable instead of checking equality? There have been many times where I have accidentally assigned, rather than compared, and it would be great to have a warning!
Is there a way Xcode can warn me without having to change coding styles to:
if (YES == aVariable) {...}
Xcode already warns you if you use = instead of == in an if statement in most cases. If you're not getting the warning, tell us what version of Xcode you're using, how old your project is, and what build settings you have changed from their defaults.
My test: I created a brand new iOS app in Xcode 4.5.2 and didn't change any build settings. I just added a little code to application:didFinishLaunchingWithOptions: to trigger the warning. Here it is:
You can disable the warning by setting the compiler's -Wno-parentheses flag (but why would you want to?). You can suppress the warning in a particular case by adding an extra set of parentheses around the assignment:
if ((x = 7)) {
There are two cases where you don't get the warning by default. First, in an init method, you can assign to self, like this:
- (id)init {
if (self = [super init]) { // no warning by default
...
Second, in any context, you can assign the result of the nextObject selector, like this:
while (object = [enumerator nextObject]) { // no warning by default
You can enable warnings in these cases by setting the compiler's -Widiomatic-parentheses flag.
The compiler flag Wparentheses should do the trick. It will force you to place parentheses around assignments in a conditional in order to compile. Clang has this flag set by default.
bool kDebuggingEnabled = NO;
...
for(i=0; i<length; i++){
...
if (kDebuggingEnabled) {
NSLog (#"Value of variable # %i",$resultingOptions);
}
}
whenever my app is live, my code checks the condition every time regarding NSLog.
Is there any better way to improve performance of my code?
You could use preprocessor macros to turn logging on and off. A good example is the DLog macro from Marcus Zarra on the Cocoa is My Girlfriend blog.
#ifdef DEBUG
#define DLog(...) NSLog(#"%s %#", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__])
#else
#define DLog(...) do { } while (0)
#endif
You would place the above in your prefix.pch file and then simply replace NSLog statements with DLog statements. You also need to make sure that DEBUG is set in your debug build configuration.
Using preprocessor macros like this means that the logging code does not get compiled into your release build so there is no performance hit for the log statements.
The blog post also contains some other useful macros for handling assertions.
You have 3 choices:
1) if you want to enable/disable your logs at build time. The preprocessor solutions are the best:
// define MY_ENABLE_LOGS in your build settings for the debug configuration only, for example
#ifdef MY_ENABLE_LOGS
#define MYLog(...) NSLog(__VA_ARGS__)
#else
#define MyLog(...) do { } while(0)
#endif
2) if you want to be able to enable/disable logs at runtime (e.g. based on some hidden preferences), your solution is likely the best. You can try to hint the compiler to optimize things a bit, though:
// tell the compiler it's unlikely that kDebuggingEnabled will be true
#define MYLog(...) do { if (__builtin_expect(kDebuggingEnabled, 0)) { NSLog(__VA_ARGS__); } } while(0)
3) last option is a little bit more complicated but can provide richer info than just logs and only depends on the kind of logging you expect to provide. The idea is to use custom probes with dtrace (which can also be used within Instruments). This only works in OS X (not in iOS). See http://www.macresearch.org/tuning-cocoa-applications-using-dtrace-custom-static-probes-and-instruments for example.
Note that you can mix 1) and 2) depending on your needs. 3) is meant to be almost zero cost when probes are not traced and can provide much richer info than a simple log string.
One caveat with these solutions: the arguments of the logs won't be evaluated when MY_ENABLE_LOGS is not defined which might change your application behavior.
Use a preprocessor macro to check if you're building for debug:
#if DEBUG
// do stuff
#end
If the preprocessor (the thing that runs before the compiler) evaluates DEBUG to be true it'll keep the code there for the compiler to compile, but if DEBUG doesn't exist or is false, it'll erase that piece of code.
I understand exactly why unused variable warnings occur. I don't want to suppress them in general, because they are incredibly useful in most cases. However, consider the following (contrived) code.
NSError *error = nil;
BOOL saved = [moc save:&error];
NSAssert1(saved, #"Dude!!1! %#!!!", error);
Xcode reports that saved is an unused variable, when of course it isn't. I suspect this is because NSAssert1 is a macro. The NS_BLOCK_ASSERTIONS macro is not defined, so Objective C assertions are definitely enabled.
While it doesn't hurt anything, I find it untidy and annoying, and I want to suppress it, but I'm not sure how to do so. Assigning the variable to itself gets rid of the compiler warning, but I'd rather do it the "right" way if such a thing exists.
I'm unsure if it's still supported in the new LLVM compiler, but GCC has an "unused" attribute you can use to suppress that warning:
BOOL saved __attribute__((unused)) = [moc save:&error];
Alternatively (in case LLVM doesn't support the above), you could split the variable declaration into a separate line, guaranteeing that the variable would be "used" whether the macro expands or not:
BOOL saved = NO;
saved = [moc save:&error];
Using Xcode 4.3.2 and found out that this seems to work (less writing)
BOOL saved __unused;
In Xcode you can set the warnings for "Unused Variables." Go to "Build Settings" for the target and filter with the word "unused"
Here is a screenshot:
I suggest you only change it for Debug. That way you don't miss anything in your release version.
NSError *error = nil;
BOOL saved = [moc save:&error];
NSAssert1(saved, #"Dude!!1! %#!!!", error);
#pragma unused(saved)
Try like this.
It is working for me. It will work for you, too.
The only simple and portable way to mark variable as used is… to use it.
BOOL saved = ...;
(void)saved; // now used
You may be happy with already described compiler-specific extensions, though.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
NSUInteger abc; /// Your unused variable
#pragma clang diagnostic pop
SOURCE
try with: __unused attribute. Works in Xcode 5
This is the way you do it in C and therefore also Objective-C.
Even though you do not have warnings enabled, it's always a good idea to mark the return value as explicitly ignored. It also goes to show other developers, that you have not just forgotten about the return value – you have indeed explicitly chosen to ignore it.
(void)[moc save:&error];
EDIT: Compilers ignore casts to void, so it should not affect performance – it's just a nice clean human annotation.
You can set "No" LLVM compliler 2.0 warning on "Release"
Make it take up two lines. Separate the declaration and default value
BOOL enabled = NO;
// ...
BOOL enabled;
enabled = NO;