C compiler macros and attributes to help you

"So what?", I hear you asking, but this will not be just another list of useful preprocessor macros to use (or not use) in your programs or libraries. I want to show you what you can do to make your code easier to understand, both for people and machines.

Some things mentioned here will be common knowledge for the seasoned C/C++/objective C programmer, but some things I found and am using today were buried deep in obscure system header files or compiler documentation. I want to spare you my journey finding them one by one and then refactoring your code to make use of it one thing at a time (you do refactor your old code don't you?).

Some things mentioned here will be compiler or vendor specific, but I will mention where it works and if it is a good idea to use it. Some things will be useful only if you run a static analyzer on your code as they are merely hints to clarify for ambiguities in the code. Others will give the compiler hints about optimization possibilities, and even others are used to make your API more readable to the humans that use it and even warn them if they are using it wrong.

Platform detection

#if defined(__linux__)
    /* Linux */
#elif !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
    #include <sys/param.h>
    #ifdef __APPLE__   
        #include "TargetConditionals.h"
        #if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
            /* iOS Simulator */
        #elif TARGET_OS_IPHONE
            /* iOS Device */
        #else
            /* OSX */
        #endif
    #elif defined(BSD)
        /* BSD */
    #endif
#elif defined(_WIN64)
    /* Windows x64 */
#elif defined(_WIN32)
    /* Windows 32 Bit */
#elif __posix
    /* At least POSIX compatible */
#elif __unix
    /* some other UNIX not catched above */
#endif

Generic

Things that work on more than one compiler and platform. Much of this will be preprocessor magic or written down in the standards, but to each thing there are caveats.

  • inline / noinline
    explicitly define if a function may be inlined. Inlining functions means to insert the function verbatim at the calling position instead of creating a branch and really call that function. This may speed up your code if used correctly but can increase generated machine code size. Only use for short "hot" functions and keep an eye on performance and size of your binary. Do not assume inlining speeds up your code. Increased code size may mean you'll probably end up ruining CPU cache locality. See also: Article of the WebKit crowd to reducing code size to speed up WebKit
  • __GNUC__
    this preprocessor macro is defined for all compilers that support the GNU C compiler extensions (at least GCC and clang do)

Apple

Things that primarily work on Apple platforms (official clang/LLVM compiler is assumed).

__attribute__((objc_designated_initializer))

apply this attribute to the init function of your class that should be called by all other init-functions instead of [super init] and thus by subclasses calling up into super You'll get a warning if you forget to call the designated initializer and so could be sure all initialization goes the same path and all internal state is really set at the end of the custom init function.

In use it looks like this:

#ifndef NS_DESIGNATED_INITIALIZER
#if __has_attribute(objc_designated_initializer)
#define NS_DESIGNATED_INITIALIZER __attribute((objc_designated_initializer))
#else
#define NS_DESIGNATED_INITIALIZER
#endif
#endif

- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithURL:(NSURL *)URL;

So if we forget to call init from initWithURL we get the following warning:

Warning: Secondary initializer should not invoke an initializer on 'super'
Warning: Secondary initializer missing a 'self' call to another initializer

instancetype

Use instancetype to return instances of an initialized class instead of id to aid the compiler type checking. On init functions it is assumed the function returns an instance of the class object on which we called init but that is not the case for class constructor methods (often provided for convenience reasons to make the code more readable), because the compiler does not know the intent of the function.

Example:

[[[NSArray alloc] init] fooBar];

leads to:

No visible @interface for `NSArray` declares the selector `fooBar`

But the following will not lead to a warning (at least if it does not use instancetype):

[[NSArray array] fooBar];

If we'd declare the convenience function the following we'd get that first warning again:

+ (instancetype)array;

NS_ENUM / CF_ENUM

This one is again used for convenience to allow the compiler and Xcode to aid you with warnings and autocompletion when using typedef enum declarations.

Usually you'll declare a set of mutually exclusive flags or states the following:

typedef enum {
    FirstItem,
    SecondItem,
    ThirdItem
} ExampleEnum;

ExampleEnum fooBar;

If you do that, the compiler decides for you what datatype is used (usually int) and you'll have no control over that. To avoid the uncertainity a lot of people used the following construct to mitigate that:

typedef enum {
    FirstItem,
    SecondItem,
    ThirdItem
};

typedef NSInteger ExampleEnum;
ExampleEnum fooBar;

But that leads to a disconnect of the type name and the possible values, so autocompletion and switch ... case warnings won't work. So Apple invented NS_ENUM that ties those things together again:

typedef NS_ENUM(NSInteger, ExampleEnum) {
    FirstItem,
    SecondItem,
    ThirdItem
};

ExampleEnum fooBar;

NS_OPTIONS / CF_OPTIONS

If you want to use an enum as some kind of flag storage where flags are not mutually exclusive there is NS_OPTIONS. It works the same way NS_ENUM works but allows the variables of the defined type to contain combined values of multiple flags added together (= logical OR).

So if you have something like this:

typedef enum {
    FirstFlag  = 0,
    SecondFlag = (1 << 0),
    ThirdItem  = (1 << 1),
    FourthItem = (1 << 2)
} ExampleFlags;

ExampleFlags fooBar;

instead use this:

typedef NS_OPTIONS(NSInteger, ExampleFlags) {
    FirstFlag  = 0,
    SecondFlag = (1 << 0),
    ThirdItem  = (1 << 1),
    FourthItem = (1 << 2)
};

ExampleFlags fooBar;

NS_REQUIRES_SUPER

Mark a function in any of your class that it needs a call to super to work correctly if subclassed and overridden. This just adds a warning if you forget to call super on the subclass. Can be a lifesaver when you'll have to debug strange behaviour or ship libraries. It tells the user of the function to make sure he knows what he's doing when he overrides the function.

Just append to your function declaration:

- (NSString *)debugMe NS_REQUIRES_SUPER;

NS_FORMAT_FUNCTION()

Add format string typechecking, likely known from NSLog and printf, to your functions. The macro takes 2 arguments, the first one is the position of the format string argument, the second is the position of the ... element for the elements to include in the string.

Use like this:

void MyLog(NSString *fmt, ...) NS_FORMAT_FUNCTION(1,2);

So the compiler checks if the types of the arguments after the format string match the types defined in the format string. Additionally it will be checked if the format string is a constant string and a warning will be issued if it is not.