I've below code in my project dlog should print the values in console if isConsoleLogActive is YES.
It gives error like Operator 'defined' requires an identifier
#if defined ([Util isConsoleLogActive])// Operator 'defined' requires an identifier in this line
#define DLog(...) NSLog(__VA_ARGS__)
#define DTrace() NSLog(#"%s", __PRETTY_FUNCTION__)
#else
#define DLog(...) /* */
#define DTrace() /* */
#endif
if I use the same code([Util isConsoleLogActive]) in my .m it works perfectly fine. I face this issue only in #define
What could be the issue. Please give me some idea.
The various commands that start with # are preprocessor directives. These get executed before the compilation phase at build time, before your application actually executes. You should use the preprocessor directives to conditionally include different code in your application based on build configuration. The preprocessor, however, is the wrong way to handle conditional execution on a specific platform at runtime; for that, you want your standard "if...else" logic.
If your goal with that statement is to determine if the given selector exists, try respondsToSelector, instead.
Result of
[Util isConsoleLogActive]
is not known at compile-time. So you can not use it with '#if defined'.
Related
I'm trying to add support for the new logging and activity tracing APIs to a library in a way that maintains backward compatibility for users of the library who haven't yet adopted the latest version of the OS (iOS or macOS). I'm defining custom logging macros for each level of logging, and then for older OSes, falling back to NSLog. I've gotten this working, with one problem.
The new APIs require you to mark any non-constant, non-scalar values as explicitly public if you want them to show up in log output. This is what an invocation of my macro looks like:
UZKLogInfo("Reading file %{public}# from archive", fileName);
This compiles fine with the SDK that includes os_log (e.g. iOS 10.0 or later), but when I compile with an earlier version so my macro falls back to NSLog, I get a compiler warning:
Using 'public' format specifier annotation outside of os_log()/os_trace()
And the log line printed looks like this:
Reading file <decode: missing data> from archive
This is a simplified version of my macro definition (only including the info definition and simplifying the conditional:
#if UNIFIED_LOGGING_SUPPORTED
#import os.log;
#define UZKLogInfo(format, ...) os_log_info(OS_LOG_DEFAULT, format, ##__VA_ARGS__);
#else // Fall back to regular NSLog
#define UZKLogInfo(format, ...) NSLog(#format, ##__VA_ARGS__);
#endif
Is there any way to strip the "{public}" text (some kind of string replacement?) from format in the fallback case? Or is there another way to support the old and new APIs without giving up the level of info I've always shown in the logs? I need to use a macro (according the last year's WWDC session on the topic, or else I lose the call site metadata.
I chose to do an NSString replace in the macro, and to suppress the compiler warnings as part of it, so it could be done for each line, rather than globally for the whole file or project. It looks like this:
#if UNIFIED_LOGGING_SUPPORTED
#import os.log;
#define UZKLogInfo(format, ...) os_log_info(OS_LOG_DEFAULT, format, ##__VA_ARGS__);
#else // Fall back to regular NSLog
#define _removeLogFormatTokens(format) [#format stringByReplacingOccurrencesOfString:#"{public}" withString:#""]
#define _stringify(a) #a
#define _nsLogWithoutWarnings(format, ...) \
_Pragma( _stringify( clang diagnostic push ) ) \
_Pragma( _stringify( clang diagnostic ignored "-Wformat-nonliteral" ) ) \
_Pragma( _stringify( clang diagnostic ignored "-Wformat-security" ) ) \
NSLog(_removeLogFormatTokens(format), ##__VA_ARGS__); \
_Pragma( _stringify( clang diagnostic pop ) )
#define UZKLogInfo(format, ...) _nsLogWithoutWarnings(format, ##__VA_ARGS__);
#endif
It's called like so:
UZKLogInfo("Message: %#", anObjectToLog);
I have string constant declared as below. This code compilers in normal app debug and release target. But in app's test target (Ctrl + U), I got error that unexpected '#' in program in test target. Using xcode 7.
Modules are enabled as answered in this question.
HeaderA.h
#define URL_A "http://www.example.com/service"
HeaderB.h
#define URL_AC #URL_A
Got error here, unexpected '#' in program. Expanded from macro #URL_AC
Modules are enabled as answered in this question.
Used in an .m file as,
NSURL * url = [NSURL URLWithString:URL_AC];
Even declaration of
#define URL_AC #"https://www.example.com/service?format=json"
got 'Use of undeclared identifier URL_AC' compiler error.
I found the problem. The 'define' have some #ifdef macros. And this is controlled using preprocessor declaration in the build settings. In my test target it was not defined. When I defined it, compiler gets the 'define' and all errors gone.
#if defined(TEST0)
#define URL0 "http://www.example.com"
#elif defined(TEST1)
#define URL1 "http://www.example2.com"
#endif
What is the purpose of using #define to define a constant with no value?
Such as:
#define TOKEN
You can use it to enable or disable certain code using #ifdef or #ifndef, eg this:
#ifdef TOKEN
printf("token is defined");
#endif
One use for this would be to toggle logging in debug builds. Another would be include guards.
As #Nutomic says, this sort of #define is useful for enabling/disabling certain bits of code. I've seen this used to create compile-time options for a given library:
#ifdef STRICT_PARSING
//...
#endif
#ifdef APPROXIMATE_PARSING
//...
#endif
#ifdef UNICODE //this is especially useful when trying to control Unicode vs ANSI builds
//call Unicode functions here
#else
//call Ansi functions here
#endif
#ifdef USE_STL //another good one, used to activate or deactivate STL-based bits of an API; the function declarations were dependent on USE_STL being defined. A plain-C API was exposed either way, but the STL support was nice too.
std::string MyApi();
#endif
Hi in one of my applications I have to support that app for IOS6 & IOS7.Inorder to accomplish that first I have to know the current device version. For that I had defined one macro and I am trying to using that macro as a reference to accomplish my task. The code which I wrote is as such below.
In .h file I defined IPhoneOSVersion as 50000.
This code is in .m file
if([[[UIDevice currentDevice] systemVersion] isEqualToString:#"7.0"])
{
#undef IPhoneOSVersion
#define IPhoneOSVersion 70000
NSLog(#"_IPHONE_OS_VERSION_MIN_REQUIRED after is %d",IPhoneOSVersion);
}
else
{
#undef IPhoneOSVersion
#define IPhoneOSVersion 60000
NSLog(#"_IPHONE_OS_VERSION_MIN_REQUIRED after is %d",IPhoneOSVersion);
}
NSLog(#"_IPHONE_OS_VERSION_MIN_REQUIRED after is %d",IPhoneOSVersion);
And if i run this code in IOS7. In console the data have to print like this _IPHONE_OS_VERSION_MIN_REQUIRED after is 70000 but unfortunately I am getting _IPHONE_OS_VERSION_MIN_REQUIRED after is 60000. Even I put a break points at else condition also but that is not executing but the macro value is changing.Can anyone please let me know why the macro value changing like this.
You shouldn't be hardcoding against the OS version, Apple recommended way of supporting multiple OS versions is to check for some specific class, API, protocol or function, this allows for greater flexibility as some of that stuff is sometimes backwards compatible.
Here's a pretty decent tutorial on how to check for existence of specific resources in code http://www.raywenderlich.com/42591/supporting-multiple-ios-versions-and-devices and the docs from Apple https://developer.apple.com/library/ios/documentation/developertools/conceptual/cross_development/Using/using.html
EDIT: To answer your question on why the macro is changed, the compiler goes over both branches of the if-else, thus the last declaration of the macro is used. You can't use a macro like that and change it during runtime, macros are meant to be define before compilation.
You use the preprocessor in Objective-C in exactly the same way as in C or C++. The preprocessor doesn't care about your if/else statements. It sees a sequence of #undef, #define, #undef, #define and performs them one after the other, so in your last line, the last #define is in effect. You cannot influence these #defines with anything happening at runtime.
There are always three OS versions in play: The deployment target (that is the lowest OS version where you allow your app to run), the SDK version, and the actual version at runtime. The first two you set in Xcode; the actual version is obviously out of your control except that you know it is the same or higher than the deployment target.
__IPHONE_OS_VERSION_MIN_REQUIRED = Deployment target
__IPHONE_OS_VERSION_MAX_ALLOWED = SDK version
Try with
if([[[UIDevice currentDevice] systemVersion] floatValue] == 7.0)
It is unclear to me when using compiler directives which of the below two code snippets is correct/preferred and why. It seems that most developers and Open Source projects I've seen use the first but I have seen the second used frequently as well.
#ifdef DEBUG
[self doSomethingOnlyWhenDebugging];
#endif
VERSUS
#if DEBUG
[self doSomethingOnlyWhenDebugging];
#endif
Which of the above code snippets is preferable for running code only while debugging and why? My guess is that the first will run if DEBUG is defined as TRUE or FALSE where the second will run only if DEBUG is defined and set to TRUE. Is that correct?
You are correct. #if DEBUG will not evaluate if DEBUG is defined as 0.
As for when to use each, you can stick to using #ifdef for anything where you only need to add code if the preprocessor definition is present, such as adding debug logging. If you need to inspect the value and go down different compilation paths, then I would use a 0 or 1. A good example of that is TARGET_IPHONE_SIMULATOR, which is always defined for an iOS project, but only 1 if you’re compiling for the simulator.
As I know, the finest choice is:
#ifndef DEBUG
NSLog(#"-1");
#elif DEBUG == 0
NSLog(#"0");
#else
NSLog(#"%d", DEBUG);
#endif
then, you will know #ifndef DEBUG is preferred above all others.
There is a simpler choice:
#if DEBUG == 0 // DEBUG is not defined or defined to be 0
// do sth
#else
// do sth
#endif
However, if -Wundef compiler flag is on, there might be a warning with #if DEBUG == 0.
You need to look at the code where DEBUG gets defined or not defined, and write your code accordingly. With DEBUG, you will find that it is either not defined, or defined with a value of 1. So either #if DEBUG or #ifdef DEBUG will work.
For #define's that are under your control, I recommend that you always define them, either with a value of 0 or 1. You can then use #if to check for the value, but you can also use them directly in an ordinary if statement or in an expression, which may make your code more readable. And since it is always defined, you can use "Jump To Definition" in Xcode to go to the place where it is defined and check how and why it is set. If instead you either #define or not #define the value, and it is not defined, then Xcode will have no idea where in your source code it is not defined. It also gives you a chance to look for misspelled uses. If "Jump to Definition" doesn't work, then you know you have spelled it wrong.
BTW Within an #if directive, any macro that is not defined is replaced by 0. So #if DEBUG will work if DEBUG is not defined, or if DEBUG is defined as 0, and it will not compile if DEBUG is defined as nothing - which will tell you what is wrong so you can fix it. From that point of view, using #if is better unless it doesn't compile.