As of Xcode 10.2, there is a new macro for specifying enums in Objective-C, NS_CLOSED_ENUM. Using this in place of NS_ENUM is required, to allow those enums to be used in Swift, as if they were declared in Swift (meaning without either requiring an #unknown handler in switch statements, or getting a warning). See here for more.
It appears that clang-format has not caught up to this new reality, and won't recognize
typedef NS_CLOSED_ENUM(...
the same as it does
typedef NS_ENUM(...
Is this true?
As of these commits (around July 2019), clang-format apparently does support NS_CLOSED_ENUM.
Related
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.
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.)
Swift 2 have API availability checking.
The compiler will give you an error when using an API too new for your
minimum target OS
Why can't the objective-c compiler do the equivalent?
I googled objective c API availability checking and only swift 2 results came out so I assume the compiler for objective c can't do that.
Xcode 9.0 brings the runtime availability checking syntax from Swift to Objective-C:
if (#available(macOS 10.9, *))
{
// call 10.9+ API
}
else
{
// fallback code
}
this comes complete with warnings for calling APIs that are newer than your deployment target (if those calls are not wrapped in checks).
finally ;)
The warning (Swift makes it an error) just hadn't been implemented in the Clang compiler for years, but it's not an inherent Objective-C limitation (although due to its dynamic nature, you won't be able to catch all cases), nor Swift terminology.
The Apple macros (e.g., NS_CLASS_AVAILABLE) and source attributes (__attribute__((visibility(...))), __attribute__((availability(...)))) to annotate headers with availability information have been there for years, and they are widely-used in Apple's SDKs. The macros are defined in Foundation's NSObjCRuntime.h, and the Availability.h/AvailabilityMacros.h system headers, and the compiler can (and does) read them.
In early 2015, the -Wpartial-availability warning has been added to Clang's master branch, but this commit/warning hadn't made its way into Apple's version of Clang until (including) Xcode 7.2. You will get an unknown warning option log when adding the warning flag to a project in Xcode 7.2, but the flag is available in Xcode 7.3. There's currently no predefined setting for it, but you can add the flag to Other Warning Flags under Build Settings.
There are other tools that use LLVM libraries to detect partially available APIs, e.g., Deploymate. For my diploma thesis, I developed a tool that integrates directly into Xcode and is based on a modification to the Clang compiler. The code is still online, but I haven't kept up with the general Clang development so it won't be of much use, except for learning purposes. However, the "official" code (linked above) is much cleaner and better.
Edit: Starting with Xcode 9, availability checking will work for Objective-C (and C), too. Instead of using the above-mentioned warning flag, which does not support raising the deployment target temporarily/locally and therefore causes plenty of false positives, there's -Wunguarded-availability, and if (#available(iOS 11, *)) {...} to check and raise the deployment target for the following block of code. It is off by default, but -Wunguarded-availability-new will be on by default, and starts checking anything beyond iOS/tvOS 11, watchOS 4, and High Sierra. More details on that can be found in the Xcode 9 beta release notes, which currently requires signing in with a developer account.
Objective C does not have availability checking as part of the language, as the same result is available via Objective C preprocessor.
That is the "traditional" way of doing that in C derived languages.
Want to know if compiled in debug mode?
#ifdef DEBUG
// code which will be inserted only if compiled in debug mode
#endif
Want to check at compile time for a minimum version?
Use the Availability.h header in iOS, and similar headers for Mac OS X.
This file reside in the /usr/include directory.
just test __IPHONE_OS_VERSION_MAX_ALLOWED with the preprocessor, e.g.:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)]) {
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert) categories:nil]];
}else{
[[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert)];
}
#else
[[UIApplication sharedApplication] registerUserNotificationSettings: (UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert)];
#endif
As Swift does not have a preprocessor, they had to invent a way of doing these kind of checks within the language itself.
If you want to check availability of a method at runtime, please notice that the appropriate way is by using the method respondsToSelector:, or instancesRespondToSelector: (the latter at class level).
You will normally want to combine both approaches, compile time conditional compilation and runtime check.
Objective C method presence verification, e.g. at class level:
if ([UIImagePickerController instancesRespondToSelector:
#selector (availableCaptureModesForCameraDevice:)]) {
// Method is available for use.
// Your code can check if video capture is available and,
// if it is, offer that option.
} else {
// Method is not available.
// Alternate code to use only still image capture.
}
If you want to test if a C function exists at runtime, it is even simpler: if it exists, the function itself it is not null.
You can't use the same identical approach in both languages.
It does these days. Furthermore, with Xcode 11 (including the current Xcode 11.3.1), you can even get it from the snippets. Press the + button towards the top right of Xcode (as shown in the image below).
Then in the search box, type "API". All 3 versions of the snippet for API Availability Check will appear -- Objective C, C and Swift.
Of course, you will get errors in Objective-C code. But you won't find results in google for Objective-C, if you use a term defined for Swift as you will not find kisuaheli website in google if you search for a german word. ;-)
You will get an error linking Objective-C code against a too old SDK. This is simply because the used method or class or $whatever is not defined in the header for that SDK. Again, of course.
This is typical Swift marketing of Apple: Because of the incapability of Swift they have to extend the language to get something, which is quite easy in Objective-C. Instead of clarifying that this is the result of the poorness of Swift, they tell you that this is a great feature of Swift. It is like cutting your fingers and then saying: "We have the great plaster feature!!!!!!!!" And you have to wait only some days and one comes around on SO with the Q: "Why does Objective-C does not have the great plaster feature???????" The simple answer: It does not cut your fingers.
The problem is not to generate the errors. The problem is to have one source code for all versions, so you can simply change the SDK version and get new code (or errors). You need that for easier maintenance.
In Objective-C you simply can use the answer found here:
Conditionally Hide Code from the Compiler or you can do that at runtime as mentioned in the comments to the Q. (But this is a different solution of the problem by nature, because it a dynamic approach instead of a static one as you have to do in Swift.)
The reason for having a language feature in Swift is that Swift has no preprocessor, so the Objective-C solution would not work in Swift. Therefore conditional code would be impossible in Swift and they had to add the plaster, eh, language feature.
I'm trying to compile using the LLVM GCC 4.0 compiler, and I get this error in multiple of my .m files: ARC forbids explicit message send of 'release'
I've tried using -fno-objc-arc as a compiler flag but that returns the error: Unrecognized command line option "-fno-objc-arc".
How can I solve this?
Simply remove all calls to -release. You're not allowed to call -release under ARC because the compiler will insert all the necessary retain/release calls for you. Read more about ARC here.
The other way is to use the conversion tool, and Xcode can convert your project to ARC (including removing these calls) for you:
To disable ARC entirely, change your build settings:
However I'd recommend you start using ARC, it will make things a lot easier and actually faster too.
If you happen to be using the ASI (http://allseeing-i.com/ASIHTTPRequest/How-to-use) api, refer to this answer as it needs to be built using -fno-obj-arc flags for specific classes... Files doesn't support the ARC feature, how to deal with and refer to the answer titled 'Disable ARC for that one class'
When trying to use -retain, -release, and -dealloc while building my application using automatic reference counting in Xcode 4.2, I get an error like the following:
Automatic Reference Counting forbids
explicit message send of 'dealloc'
Why am I seeing this error? Are -retain, -release, and -dealloc no longer allowed under automatic reference counting?
Basically:
When using ARC, it's all or nothing. Either the compiler is managing all of the retains/releases/deallocs for you, or it is doing nothing. You cannot intersperse your own calls to them, because the compiler wants to do it all itself. It can make absurd optimizations by doing this (for example, a method that returned an autoreleased object under Manual Memory Management may now produce an object that never ends up in an autorelease pool). If you were to start sprinkling in your own calls to retain and release, then the compiler would have to work with these and wouldn't be able to perform a lot of the optimizations that it wants (and that you should want).
And as an added bonus, invoking -retainCount is now a compiler error! OH HAPPY DAY!
in response to AliSoftware: we CNA mix ARTC and not-ARC frameworks, and also arc and not-ARC source.
(I did it..)
The basic ideas are:
1) compiler will insert/remove calls as a very-very-good cocoa programmer can do
2) cocoa is ANYWAY C code, so we have separate compilations, so the linker CAN link binaries produced by multiple sources. Think it as we can mix asm and cocoa, or C and pascal...
in main opinion the Appleadvantege over c# / java is here: we are alway allows to mix, and, using a COMPILER technique 8non a runtime..) we can rush performance beyond.
Under automatic reference counting, retain, release, and dealloc are not allowed.
If you have an existing code, you can keep using it as is with the -fno-objc-arc you can selectively disable ARC on any file.
If you want to disable ARC on MULTIPLE files:
Select desired files at Target/Build Phases/Compile Sources in Xcode
PRESS ENTER. (double click selects only one file)
Type -fno-objc-arc
Press Enter or Done
As I have pointed out in my answer on Xcode ARC, you can compile specific source files as non-ARC. Dave DeLong's answer is a bit off. It doesn't include the fact that you can instruct the compiler to compile source as non-ARC in an ARC-enabled project (as explained here).