Swift Compiler Flags Wont affect Objective-C code - objective-c

under Active Compilation Conditions i have added a new flag (for debug only) called SOMEFLAG.
the check is implemented like so and works great:
#if SOMEFLAG
print("SOMEFLAG is true");
#endif
it actually prints like i would expect. now, i need to examine the same flag from Objective-C class and it never evaluates to true. any idea why?
should i make this flag in other way?
in general my goal is to be able to detect when it is executed from another target, the build is triggered from the cli.
if there is other way that less limiting i would like to know.
thanks!

In Objective-C (and other C-like languages that use a preprocessor) the canonical way to check whether a symbol is defined would be using #ifdef, not #if. There is an #if directive in Objective-C, but it doesn't behave quite the same way as the Swift #if compiler directive.

Related

Swift conditional compiling didn't work properly when I use macro defined in objective-c

I define a simple macro in objective-c header file, and import this header file into Swift through project bridging header. I was able to use this macro as a constant in Swift, but when I use it to do conditional compiling, it doesn't work properly.
I create a simple project in Xcode 10.2.1 and add some code to reproduce it.
In ViewController.h
#define TEST_FLAG 1
#interface ViewController : UIViewController
#end
In ViewController.m
#import "testMacro-Swift.h"
- (void)viewDidLoad {
[super viewDidLoad];
SwiftClass *s = [[SwiftClass alloc] init];
[s printMSG];
#if TEST_FLAG
NSLog(#"Objc works.");
#endif
}
In testMacro-Bridging-Header.h
#import "ViewController.h"
SwiftFile
#objc class SwiftClass: NSObject {
#objc func printMSG() {
print("Macro \(TEST_FLAG)")
#if TEST_FLAG
print("compiled XXXxXXXXX")
#endif
}
}
Console Output
Macro 1
2019-07-03 14:38:07.370231-0700 testMacro[71724:11911063] Objc works.
I expected compiled XXXxXXXXX to be printed after Macro 1, but it not.
I am curious why this will happen.
My project is mixed with objc and swift. I don't want to declare a same flag in swift.
Based on this Apple article, https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_macros_in_swift, simple C (and Objective-C) macros are imported into Swift as global constants. This is demonstrated by the output from your line
print("Macro \(TEST_FLAG)")
The snippet
#if TEST_FLAG
print("compiled XXXxXXXXX")
#endif
uses a different TEST_FLAG, which is a Swift preprocessor flag. You could define it under Build Settings -> Active Compilation Conditions as TEST_FLAG or under Build Settings -> Other Swift Flags as -DTEST_FLAG.
The above explains why this happens. I can't think of a simple way to avoid defining the same flag separately for Objective-C and Swift preprocessor in Xcode. If you just want to control whether some Swift code is executed based on the TEST_FLAG, you can do something like this:
if TEST_FLAG != 0 {
print("compiled XXXxXXXXX")
}
However, if you want to control compilation of the code, then you may have to use separate TEST_FLAGs for Objective-C and Swift and make sure they are consistent. To aid in making them consistent, you could set the TEST_FLAG used by Objective-C code in Other C Flags, which allow you to define different flags for different SDKs, Architectures, and build types (Release/Debug). Active Compilation Conditions allow the same flexibility.
Another trick to facilitate consistency between (Objective-)C and Swift compiler flags is to create a new user-defined build setting: click on + to the left of the search box under Build Settings.
Say, call it COMMON_TEST_FLAG and set its value to TEST_FLAG. Then add -D$(COMMON_TEST_FLAG) to Other C Flags and Other Swift Flags. Now when you build your code TEST_FLAG will be defined in both Objective-C and Swift code within your target. If you don't want it to be defined, just change the value of COMMON_TEST_FLAG to something else. A couple of things to watch for, though:
You cannot make COMMON_TEST_FLAG empty: this will cause the other
flags to be just -D, leading to a build error.
Make sure the value of COMMON_TEST_FLAGdoes not conflict with
macros defined elsewhere.
Swift's preprocessor is (intentionally) waaaaaaay more limited than C's. Macros come with really serious draw backs.
They define regions of code that aren't active in every build target. Because of this, your tests won't be hitting every branch (or even compiling every branch). This quickly becomes a maintenance disaster. For n unique flags, there are 2^npossible sets of values and thus,2^n` possible builds. Do you have to test each of them? Maybe, maybe not, but even just reasoning about what to test isn't easy.
They can get tangled up and incredibly complex, especially with nested blocks.
In general, try to express as much of your code in the main programming language (C, ObjC, Swift), and resort to using macros only when there's a good reason, such as:
Reducing repetition in a way that can't be done with a function.
Improving performance in a critical region of code (by forcing inlining of macro code, e.g. #define max(a, b) ((a) < (b) ? (b) : (a))). Though this is exceptionally rare.
Expressing a piece of logic that can't be expressed in the language. For example, there's no way to express if #available(...) in Swift, without using the preprocessor.
Your example doesn't meet any of these criteria. It's increasing repetition, it's not performance-critical, and it's not doing something that can't be done in regular Swift code.
A much better approach to this (in both Swift and Objective C), is to create a logger that is initialized with different configurations between debug and release builds. Your viewDidLoad method should not concern itself whether or not TEST_FLAG is set. View controllers should control views, not make decisions as to what things should be logged. It should just call the logger to send off whatever log messages it wants to send, and leave it up to the logger to decide how to log those messages (to an output stream, file, in-memory circular buffer, database, stream to an analytics API, ignore them, etc.)

How to programmatically detect automatic reference counting?

This might be a silly question, but I just wanted to know. I want my code to detect if ARC is enabled, programmatically. How do I do that? Is there is any flag that I could check? Actually the problem is that, I have written an open-source library. I have used release and retain in it. if some one else uses my library files using ARC enabled, I dont want them to get any errors. how do I achieve this? or is there any possible ways I can provide any tools to compile my library before using it?
#if !__has_feature(objc_arc)
//Do manual memory management...
#else
//Usually do nothing...
#endif
This is of course a compile-time check, you cannot check for ARC at runtime.
An alternative would be to set the -fno-objc-arc compiler flag for your files that use manual memory management in a project that otherwise uses ARC.
Whether you want to bother with this at all or just use ARC everywhere depends on how backward-compatible you want/need to be. Code that supports both ARC and MRC can get quite hard to read and maintain.
You don't detect it programmatically, it operates based on translations. That is, it is not like Garbage Collection -- which is process-wide, required all linked libraries to support (and implement it correctly in that mode). You can have some files compiled with ARC, and some without.
However, you can detect it at compilation.
As far as the distribution of your library: I would not bother with a translation based system, where ref count ops are conditionally enabled. I would (personally) just support one model (MRC in your case, until you choose to migrate it to ARC), then expect people to link to the library, or if they compile it in a target they configure, to disable ARC. Conditionally enabling/disabling code based on the presence of a feature is asking for tough bugs, particularly when it's likely to affect 9% of your library's lines of code.
NO, you can't, Xcode would not compile in ARC projects if your source uses retain-release

Is it possible to create custom directives in Objective-C?

Objective-C has directives like:
#interface
#implementation
#end
#protocol
#property
#synthesize
I think of these things like sophisticated marco or code-generators. Is it possible to create custom directives for code-generation purposes? One possible use is generating methods for CoreData.
I'm thinking not, because I've never seen anything about it, but my world isn't the world.
Followup Question:
Jonathan mentioned below that it is possible to write your own preprocessor and this begs the question of how. Currently, #define SYMBOLIC_CONSTANT 102 will replace all instances of the characters SYMBOLIC_CONSTANT with the characters 102 in the file before the files moves on to the compiler.
I know it XCode you can add a "Run Script Phase" to a Targets build process. So I could write a script to find my custom preprocess directives like '$coredata' and then have the script generate a new file that with the characters $coredata replaced with some characters of code. But from what I understand of XCode's build process you can't feed altered files into the Compiler Sources phase. The files are specified and locked by the IDE.
Has anyone done something similar? I know it's possible with external build system, but to be honest I'm not at that level of understanding. I don't know the technical details of what the Build and Run button does.
In the meantime, I'll start reading Apple's XCode Documentation...
Thanks for the responses!
While accepted answer is right, there is a partial hacky solution to this kind of a problem, which libextobjc library adopts. Consider this code, you will find the definitions like the following there:
#define weakify(...) \
try {} #finally {} \
metamacro_foreach_cxt(ext_weakify_,, __weak, __VA_ARGS__)
Such definition allows using weakify keyword in the following form:
id foo = [[NSObject alloc] init];
id bar = [[NSObject alloc] init];
#weakify(foo, bar);
The author of library explains it here:
Since the macros are intended to be used with an # preceding them
(like #strongify(self);), the try {} soaks up the symbol so it doesn't
cause syntax errors.
Updated later
From now on libextobjc uses #autoreleasepool to "soak up the symbol".
Your thinking is correct: it is impossible to do this in your code. The only way to add more #-directives is via the compiler itself. Even if you went to all that trouble, I can almost guarantee that the syntax highlighting support for them is hard-coded into an Xcode configuration file somewhere.
Oh, and if you were considering the use a pre-processor macro, it is my understanding that the # character is illegal in pre-processor macros.
Edit: I ran a test, and I am correct. Using the # character in a C preprocessor macro is illegal. They follow the same rule as variable names.
You mean within the bounds of Objective-C? No, as it has no way to recognize your new keywords. You could write a preprocessor to detect #whatever and convert it to code, but if you tell us what specifically you'd like to do, we may be able to suggest a more efficient or optimal approach.
It is not possible. These are keywords built into the Objective-C language. Just because there is an # in front of them doesn't make them different from other keywords.

Why the macros in Objective-C / Cocoa?

I'm coming from a place without macros (Java/Python/C#/Scala) so perhaps my perspective is distorted but...
Why are macros used in Cocoa? Two that spring to mind are NSLocalizedString and NSAssert (and STAssert). Would it be so hard / unsuitable to make them functions (which could be inlined)? I suppose I find them a little bizarre as an unnecessary throw-back to C (and yes, I am familiar with the pedigree of Obj-C).
Is it just something that was done back in the day or is there a specific reason?
In general it is preferable to avoid macros, but for functionality like that provided by NSAssert(), you can't.
If you look at the definition, you'll see that it uses __LINE__, __FILE__, _cmd and self from the context it is used in. If it wasn't a macro, it wouldn't have access to that information.
For NSLocalizedString however, i see no obvious reason.
NSLocalizedString is a convenience macro and could probably be made a function.
However, there are good reasons why NSAssert and friends are macros besides the one given in Georg's answer. NSAssert evaluates a boolean expression to determine whether an exception should be thrown. That boolean expression might be quite computationally expensive and not the sort of thing you want to happen in your release code. There's no mechanism in C (and therefore Objective-C) of omitting normal function calls for a release build without resorting to the preprocessor or perhaps using stuff like:
if (debug)
{
assertAsAFunction(foo == 3, "foo should be 3");
}
Another advantage of the NSAssert macro is that the condition can be stringified for use in a log message.

NS_INLINE and Xcode debugger

Single stepping through code that uses any of the NS_INLINE functions from NSGeometry.h causes the debugger to lose sync with the current instruction pointer, making debugging the routines very difficult.
I've tried #undef NS_INLINE at the top of my implementation file,#define NS_INLINE in the precompiled header, looking for pragmas, compiler switches, etc., but no matter what, the functions always compile inline for my debug builds.
FWIW - NSMakeRect, NS_MakeSize, etc. all compile inline.
Question is, how do I get NS_INLINE to compile to nothing?
NS_INLINE is wrapped in #if !defined(NS_INLINE). You just need to define it appropriately before you include the Foundation headers. Glancing at the original declaration, you'll probably just need to remove __attribute__((always_inline)) for the debugger to catch your symbols (assuming you're generating all debug symbols and running a debug build - if not, then you could do a little more work to get them to all be visible. Ideally, you'll just create your own label local to your project/group/libs so you can debug your own code more easily.
Is this really about stepping through built-in API functions, or are you trying to do something similar in your own code? If it's the former, is it just due to curiosity, or some other reason for debugging? If it's the latter, I'd suggest commenting out the NS_INLINE while debugging. If you're trying to change the inlining behavior of existing API functions, you may be disappointed, and there are probably better ways to go about it. If your intent is something else, please clarify so we can answer adequately.