Swift conditional compiling didn't work properly when I use macro defined in objective-c - 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.)

Related

Swift Compiler Flags Wont affect Objective-C code

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.

Why is #import deprecated?

I understand how #import works vs #include, the whole "Only include it if it isn't already included" thing. I also understand that Apple is pretty fond of #import for their Objective-C code, so over in Xcode it's not going anywhere and everything's fine.
What I don't get is in other environments like Android Studio, it says that #import is deprecated and gives warnings if you use it. Why is that?
It's my understanding that #import is functionally different and, in some cases, arguably better than #include, depending on what you need. If that's the case, why deprecate a potentially useful tool, especially if there isn't any sign of it being replaced with something better?
Is there some horrendous flaw in it that makes it wholly undesirable to use, or is it fine?
Because Objective-C is a superset of the C programming language, the #import statement is a minor refinement upon C’s #include statement. The #include statement is very simple; it copies everything it finds in the included file into your code during compilation. This can sometimes cause significant problems. This can creates a loop, and can confuse the compiler. To deal with this, C programmers have to write guards against this type of event from occurring.
When using #import, you don’t need to worry about this issue or write header guards to avoid it. However, #import is still just a glorified copy-and-paste action, causing slow compilation time among a host of other smaller but still very dangerous issues (such as an included file overriding something you have declared elsewhere in your own code.)
From iOS 7, Apple introduced a new way to import file:
#import UIKit;
It's a new feature called Modules or "semantic import". Modules are an attempt to get around above issue. They are no longer a copy-and-paste into source code, but a serialized representation of the included files that can be imported into your source code only when and where they’re needed. By using modules, code will generally compile faster, and be safer than using either #include or #import.
To get more information about this, you can watch WWDC 2013

decompiling Objective-C preprocessor statements

Please forgive me if this is an obvious question or if there are any errors. I am very new to Objective-C and have kind of been thrown in the deep end.
I am looking into Objective-C obfuscation. On simple method of this that I found here is to user the preprocessor to change method names to gibberish. My question is whether a decompiler can recognize preprocessor statements, such that it would be able to decompile the source back to the original method names. The example from the above referenced question is below:
#ifndef DEBUG
#define MyClass aqwe
#define myMethod oikl
#endif
#interface MyClass : NSObject {
}
- (void)myMethod;
Is it possible for, when not compiled for debugging, this code could be decompiled back to anything other than
#interface aqwe : NSObject {
}
- (void)oikl;
You could absolutely not un-obfuscate that. The preprocessor runs before the compiler gets its greasy paws on the code, so it's just as if you had manually gone through and replaced all occurrences of MyClass with aqwe, etc.
Although, you should ask yourself why you want to do this. It's just obfuscation remember, rather than actually securing anything about your code. People could still look and see the code that each method comprises. You're just changing the name of symbols.
You'll save yourself a lot of time, pain and trouble if you just choose to use one the many existing obfuscators availible instead of trying to reinvent the wheel.
Take a look at this thread, you'll find lot of useful information for a starter:
https://stackoverflow.com/questions/337134/what-is-the-best-net-obfuscator-on-the-market

If Protocol method is marked #required, when not implemented, why does compiler issue a warning and not an error?

Assume that:
New Protocol is declared
Method in this protocol is marked #required
Class conforms to Protocol
Class does not implement the method mentioned in Protocol
At compile time, information about this method is known: i.e. that it is required and that this class and any other classes this class may may extend do not implement it.
Why in this case the compiler issues a warning and not an error?
Errors are only issued when the compiler cannot continue because something went terribly wrong.
When calling a method in Objective-C, the method lookup is done during runtime and not during compilation, which C++ does. In Objective-C a "message" is simply sent to the object, something like obj.executeCommand("Hey, can you execute function <name> for me?"). In C++ the object will be called directly, in a way like obj.<name>(). In the case of Objective-C the executeCommand() method is called, which exists. In C++'s case the function is called but it does not exist. These are methods that are linked on the compiler level, which means they both become memory addresses rather than names. executeCommand becomes 0x12345678 but it still uses the same message ("execute function <name>").
This is probably very confusing, but it's related to the way methods are implemented in different languages.
If you feel strongly about it, why not turn on -Werror?
I don't know the real answer but here is a use case that would go against it.
What if you implemented all of the protocol methods in a category???
Main interface declaration adopts the protocol however the protocol method implementation is in a category.
This is valid code but will show compile error if compiler was that strict !!
Objective-C is a dynamic language. The idea of what an implementation is, is different to a static language.
For the most part, it's in code that most of us implement inside the #implementation ... #end block.
But what if a method is not found? Then an object has a chance deal with it dynamically.
Imagine you have an interface for a sound effect player:
#protocol FX
- (void)playBeep;
- (void)playSiren;
- (void)playHonk;
#end
An implementation could have the files Beep.mp3, Siren.mp3, Honk.mp3 to play, but instead of implementing each of the methods, it could override -forwardInvocation: and parse the selector string, something like this pseudocode:
NSString *selName = NSStringFromSelector([invocation selector]);
if ([selName startsWith:#"play"]) {
NSString filename = fileNameFromSelector(selName);
[self playSoundFileNamed:filename];
}
This may seem contrived, but once you start using the dynamic features of the language, you will start finding more and more places where it makes sense. And by sense I mean, does this effort help in the long run?
In the above case, just add a -sound* method name to the interface, and drop in a appropriately named sound file. It just works.
Another example from personal experiments: how to deal with Core Data entities in a more natural way. I want to do this:
NSArray *people = [Person findAllWithNameLike:#"B%"];
instead of mucking about with predicates, fetch requests etc.
But I don't want to define every permutation of method in code.
How about if I wanted to build an XML builder? I would look at a dynamic approach. It has served Groovy Builders well (look at Groovy/Grails for examples).
One last example: I have a traits system where I can define behaviours in the form of groups of methods and have my objects assimilate this behaviour. So, while the compiler doesn't see an implementation for the interface my object conforms to, the implementation is injected into it from a trait class, using the Objective-C runtime. Why would I do this? I find many delegate methods are boiler plate, but at the same time, a single base class for each situation is not flexible enough. Instead of cut and paste from code samples, my 'samples' compile and run :) and any changes are reflected across all projects using the trait.
To really understand why all this is available to you, it is worth playing around with a Smalltalk environment (search Pharo or Squeak). This is where Objective-C has its roots.
And finally, to stop these warnings:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wprotocol"
#implementation ... #end
#pragma clang diagnostic pop
Because there are times when there are bogus "required" methods in a poorly designed protocol.
They should have been optional but someone insisted they are "required".
Thusly making this a run time issue rather than a compile bug is very very wise.

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.