Conditional NSLog, Without decreasing Performance - objective-c

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.

Related

CocoaLumberjack log level

I don't really understand what log level means.
In Lumbejack the following log levels defined:
#define LOG_LEVEL_OFF DDLogLevelOff
#define LOG_LEVEL_ERROR DDLogLevelError
#define LOG_LEVEL_WARN DDLogLevelWarning
#define LOG_LEVEL_INFO DDLogLevelInfo
#define LOG_LEVEL_DEBUG DDLogLevelDebug
#define LOG_LEVEL_VERBOSE DDLogLevelVerbose
#define LOG_LEVEL_ALL DDLogLevelAll
What some of those mean? and how they are used?
Is those related to CocoaLumberjack all for iOS?
Also, I use the following code in my pch file:
#ifdef DEBUG
static const int ddLogLevel = LOG_LEVEL_VERBOSE;
#else
static const int ddLogLevel = LOG_LEVEL_ERROR;
#endif
What those mean? I searched the project for the ddLogLevel var and I didnt find it used anywhere. also, not in lumberjack pod.
Setting the ddLogLevel filters what messages appear from the various DDLogXXX methods.
If you set ddLogLevel to LOG_LEVEL_ALL then all DDLogXXX methods will be logged. If you set ddLogLevel to LOG_LEVEL_INFO then only Info, Warning, and Error will be logged.
Just look at the list of #define lines you show. Selecting a given value results in only messages from that level and those higher up in the list.
If you set ddLogLevel to LOG_LEVEL_INFO and you have the following two lines:
DDLogInfo("some info message");
DDLogDebug("some debug message");
Then only the first message will be logged because Debug is lower than Info.
The actual meaning of each level is somewhat subjective. Just use them consistently in your app. The most important or critical messages should have the highest levels and the least important should have the lower level.
I use DDLogError when my app encounters unexpected values or when a method that provides an NSError parameter fails. I log a relevant message and include the NSError value.
I use DDLogInfo for "I'm here" type messages.
I use DDLogDebug to log variable values.
I don't use DDLogWarn too often but you could use it for unexpected issues where there isn't an actual error but something important to note.
Those are varying degrees of logging granularity. LOG_LEVEL_ALL means any and every log will be written to the console and file that lumberjack uses. LOG_LEVEL_OFF is the other end of the extreme where no logging happens. You can use these to determine what kind of logs to show for which build. Here are some examples of use cases.
- (void)viewDidLoad
{
[super viewDidLoad];
// this log isn't particularly useful for a production build,
// but can be helpful when debugging. So we use verbose.
DDLogVerbose(#"This view controller loaded");
}
- (void)didRecieveMemoryWarning
{
// This is a bad situation, but it isn't an error really.
// Just a signal that we need to free up memory. So we use warning.
DDLogWarning(#"We are using too much memory.");
}
- (void)doSomething
{
NSError *error;
[self doSomethingElse:&error];
if (error) {
// This is definitely an error we need to handle. If you have to
// look through crash logs on a production build, you definitely
// want this log to show up there.
DDLogError(#"Encountered an error: %#", error);
}
}
In these examples, all of the logs will show up when you run your app from Xcode, but only the error log will show up in a production crash log. The ddLogLevel constant is how you determine what level of logging you want.

Is there some no op operation in objective-c that we can use just to set breakpoint

The code shouldn't have any effect
It should be doable anywhere
It doesn't generate compiler warning
Basically sometimes I want to do NSAssert. However, rather than NSAssert, I sometimes want to do my if {} my self. That way I can just easily set or unset the break point
Currently this is what I do.
if (entityName==#"Business")
{
error=nil; //Some code for breakpoint. I want something that do nothing actually.
}
It's just happen that I have a variable named error that I will no longer use. But what would be a goodalternative
I also tried [nil donothing]; but that got compiler error.
Try this:
while (false);
There are other possibilities, like if (false) or even just a line with a lone semicolon ;, but in those cases execution stops at the next instruction, possibly because the compiler simply eliminates those empty bits of code. Using while has the advantage that execution will stop on that line (assuming you put a breakpoint there, of course).
You can give breakpoints conditions, hold down option and command and click the breakpoint, you should get a pop-up. In the 'condition' field yo can enter something like
[entityName isEqualToString:#"Business"]
(Note that you should compare strings with -isEqualToString: not ==, as the latter compares the pointers not the characters in the strings themselves.)
By using the condition in the breakpoint you shouldn't need the if statement at all, and also enable and disable the check without without recompiling. There is a lot more you can do with the breakpoint settings, like have it automatically log a message or run a debugger command etc.
In C, you can have a no-op instruction simply putting a semicolon alone in a line:
if (TRUE) {
; // Neutral operation
}
In Objective-C you could do the same, the thing is your program stops before the next line, as you can see here:
you don't compare 2 strings directly; try [NSString isEqualToString:]
you can use __asm int 3;
if ([entityName isEqualToString:#"Business"]) {
__asm int 3;
}
From CFInternal.h.
#if defined(__ppc__) || defined(__ppc64__)
#define HALT asm __volatile__("trap")
#elif defined(__i386__) || defined(__x86_64__)
#if defined(__GNUC__)
#define HALT asm __volatile__("int3")
#elif defined(_MSC_VER)
#define HALT __asm int 3;
#else
#error Compiler not supported
#endif
#endif
if ([entityName isEqualToString:#"Business"]) {
HALT;
}

Conditional compilation check for framework before importing

I'm looking for a way to check if a framework exists and/or if it's classes are defined, before importing and using that framework. Specifically, the framework is Assets Library.
Currently, I'm able to do this with the Core Data framework, since that framework has a file called CoreDataDefines.h which provides a preprocessor directive _COREDATADEFINES_H. This allows me to simply check for that defintion like so:
#ifdef _COREDATADEFINES_H
#import <CoreData/CoreData.h>
// do something with Core Data
#else
// do something without using Core Data
#endif
Unfortunately, the Assets Library does not provide a clear definitions header file so I'm looking for a way to write my own #define statement that can check for the framework's existence before importing it, much like I have done for Core Data above.
I have tried this:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
// import assets library if defined !
#define ASSETSLIBRARY_DEFINE (NSClassFromString(#"ALAsset") != nil)
#if ASSETSLIBRARY_DEFINE
#import <AssetsLibrary/AssetsLibrary.h>
#endif
#endif
... but no luck.
The compiler tells me that the "Token is not a valid binary operator in a preprocessor subexpression."
Any help is always much appreciated.
If you know what class should be imported with the framework you can check if it is loaded:
BOOL isFrameworkLoaded = (NSClassFromString(#"MyClassNameFromTheFramework") != nil);
What you're doing here is very wrong. The _COREDATADEFINES_H define you see in CoreDataDefines.h is known as the sentinal value, an old C technique to avoid multiple inclusions of the same header file.
You should certainly not be using this in your own code, and the presence of the sentinal only tells you that the header has already been included somewhere else. If the sentinal is not defined, it simply means the header has not been included, not that the framework itself is nonexistant.
I'm not sure exactly what you are trying to do, but it looks like you want to use macros to decide between code that uses the framework, and code that doesn't use the framework. If you want to do this at compile time, your only choice is to define your own macros and set them up in your target with some compiler options. For example, to enable code that uses the assets library you might define this in the "Other C Flags" build setting:
-DUSE_ASSETS_FRAMEWORK
And then use this in your code:
#ifdef USE_ASSETS_FRAMEWORK
#import <AssetsLibrary/AssetsLibrary.h>
// code that uses assets framework
#else
// code that does not use assets framework
#endif
If you want to be able to detect at runtime whether the app is linked against the framework and that the framework exists on the current iOS version, you should use the standard approach that Apple recommends which is to test the existance of any classes or functions you need:
if (NSClassFromString(#"ALAsset")) {
// ALAsset is available }
} else {
// ALAsset not available
}
I have a few tricks for this kind of thing… Though what follows may not EXACTLY solve the problems you detailed, this may well aide in finding your ultimate solution… The first "strategy" can be called upon in a "Script Build Phase". This can be used in many ways, but in this example it TESTS that the Framework is valid, (according to otool, and then does some "post-processing" accordingly.. and then tests it again.. and so on..
function test { "$#"; status=$?;
if [ $status -ne 0 ]; then
echo "error with $1"; fi
return $status
}
PRODUCT_PATH="${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}"
BINARY="$PRODUCT_PATH/${PRODUCT_NAME}"
MAC="/Library/Frameworks/"
SYSTEM_PRODUCT="$MAC/${WRAPPER_NAME}"
test otool -l "$BINARY" #/Library/Frameworks/AtoZ.framework/AtoZ
if [ $status -ne 0 ]; then
sudo rm -r "$SYSTEM_PRODUCT"
sudo cp -r "$PRODUCT_PATH" "$SYSTEM_PRODUCT"
test otool -l "$BINARY"
if [ $status == 0 ]; then
cd "${PRODUCT_PATH}"
ln -sF Versions/Current/Frameworks Frameworks
fi
The next hand-me-down in this wisdom free-for-all is the sexy, and aptly named NSBundle method.. preflightAndReturnError
NSBundle *b = [NSBundle bundleWithPath:path];
NSError *e = nil;
BOOL okdok = [b preflightAndReturnError:&e];
if (okdok)
okdok = [b load];
if (!okdok) { [self jumpOffABridge:nil]; goto hell; }
Chances are though… you'll be.. OK DOK, by the time load finishes up.

How do I disable NSLog?

I'd like to do the following in Xcode:
Find all NSLog commands without comments, and replace it with //NSLog...
In other words, I want to comment all NSLog calls. Is this possible? Is there an easy way to do this?
wait there is far more simple method. Just add the following lines when you dont need NSLOG to your constants file
#define NSLog //
and comment it when you need it.
EDIT: In latest Xcode, you get error on above statement. So I figured out the best way is
#define NSLog(...)
Update:
The answer bellow is actually much better. See here.
Initial answer:
There is a little hack that you could do. Search for all NSLog and replace them with //NSLog and than do another search for ////NSLog and replace them with //NSLog.
#define NSLog if(1) NSLog
if you dont want log set 1 as 0.
I have to do a separate answer because I cant comment yet, but one thing you really need to be careful about when you do a find and replace is something like this:
if(BOOL)
NSLog(#"blah blah");
[self doSomething];
If you comment out the nslog, the conditional gets associated with the method below it, correct me if I'm wrong
The answers you have are correct for your question. But. Your real question is how to turn of NSLogs in certain conditions. i.e. you want them to show for Debug builds, and not for Release builds. In which case try defining and using the DLog() macro as described on Cocoa Is My Girlfriend. If you're using Xcode4 it's even easier because the Debug and Release builds define and undefine DEBUG so you don't have to do that.
It's a lot easier than commenting and uncommenting lines of code.
#ifdef RELEASE
#define NSLog(...) do { } while (0)
#endif
is the best way i found so far.
#define NSLog(...)
add this line into your .pch file
if you want log than comment it
You can do this in a single find and replace operation. You can just do this simple Regular Expression replace. This handles both the commented(//) and non-commented lines. This also works even if the previous commented lines has more than two forward slashes(like ///)instead of rwo. You can refer this link. Do the following steps.
Select Edit > Find > Find and Replace in Workspace
Style => Regular Expression
Type (/)*(NSLog.*) in Find field.
Do the find operation.
Type //\2 in the Replace field.
Do the replace operation.
Enjoy the beauty of regular expressions ;-)
I would do this
#define EnableNSLog 1
#if EnableNSLog == 0
#define NSLog //
#elif EnableNSLog == 1
#warning Disable NSLog
#endif
This should generate a warning message to remind me to disable NSLog before final release.
right click on NSLog statement in xcode and select "find in project" as text.you would be prompted to a new window where you can follow the guidance given by Mihai Fratu.
TNQ
in the Menu bar : Edit > Find > Find and Replace in Workspace
then, display options to use regular expressions.
search/replace for "[^/]NSLog"
How to disable NSLog in Xcode for Production stage
Add #define NSLog in appName-Prefix.pch file in Supporting Files Folder of your project
and result file code look like...
// Prefix header for all source files of the 'NSLog' target in the 'NSLog' project
//
#import <Availability.h>
#ifndef __IPHONE_4_0
#warning "This project uses features only available in iOS SDK 4.0 and later."
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#endif
//Add this to disable NSLog
#define NSLog //
You can use following Preprocessor Directives, it will not go with release mode.
So you don't have to commenting NSLog().
#ifdef DEBUG
NSLog(#"YOUR MESSAGE HERE!");
#endif
try this also:
#define NSLog(#"YOUR MESSAGE HERE!") do { } while (0)
Add the following line to your .pch file
#define NSLog
for enable comment it

How can I get rid of an "unused variable" warning in Xcode?

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;