In this post, we’ll examine what __attribute__ directives are and how they can be used in development. The goal is to establish a value to using __attribute__ directives in any codebase and to provide a starting point with some directives that anyone can start using right away.
What are __attribute__ directives?
The __attribute__ directive is used to decorate a code declaration in C, C++ and Objective-C programming languages. This gives the declared code additional attributes that would help the compiler incorporate optimizations or elicit useful warnings to the consumer of that code.
Better said, __attribute__ directives provide context. The value of providing context to code cannot be overstated. Developers have provided context by way of explicit declarations and comments since before the advent of the integrated circuit, but the value of providing context that can be evaluated by a compiler gives us a whole new level of control. By explicitly providing the confines of how an API behaves to the compiler, a programmer can gain some tangible benefits. The directives can be used to enforce compliance with how other programmers consume that API. In other cases, __attribute__ directives can help the compiler to optimize - sometimes to large performance gains.
As Mattt Thompson cogently put it in a blog post: “Context is king when it comes to compiler optimizations. By providing constraints on how to interpret your code, [you’ll increase] the chance that the generated code is as efficient as possible. Meet your compiler half-way, and you’ll always be rewarded… [It] isn’t just for the compiler either: The next person to see the code will appreciate the extra context, too. So go the extra mile for the benefit of your collaborator, successor, or just 2-years-from-now you.” Which leads nicely into wise words from Sir Paul McCartney, “and in the end, the love you take is equal to the love you make.”
When should I use an __attribute__ directive?
Whenever you have an opportunity to provide additional context to a code declaration (variable, argument, function, method, class, etc), you should. Providing context to code benefits both the compiler and the reader of the API, whether that’s another programmer or yourself at a future point in time.
Now let’s be practical for a moment too. There are dozens of __attribute__ directives and knowing every single one of them for every single compiler on every single architecture is just not a reasonable return on investment. Rather, let’s focus on a core set of commonly useful __attribute__ directives any developer can take advantage of.
Recognizing the dangers of misusing an __attribute__ directive
Just as poorly written comments and documentation can have consequences, providing the wrong __attribute__ can have consequences. In fact, since an __attribute__ affects code compilation, affixing the wrong __attribute__ to code can actually result in a bug that could be incredibly difficult to debug.
Let’s take a look at an example of where an __attribute__ directive can be misused. Let’s suppose I have an enum that I use pretty often and frequently want a string version for it, whether it’s for populating a JSON structure or just for logging. I create a simple function to help me convert that enum into an NSString.
// Header declarations typedef NS_ENUM(char, XPL802_11Protocol) { XPL802_11ProtocolA = 'a', XPL802_11ProtocolB = 'b', XPL802_11ProtocolG = 'g', XPL802_11ProtocolN = 'n' }; FOUNDATION_EXPORT NSString *XPL802_11ProtocolToString(XPL802_11Protocol protocol); // Implementation NSString *XPL802_11ProtocolToString(XPL802_11Protocol protocol) { switch(protocol) { case XPL802_11ProtocolA: return @"802.11a"; case XPL802_11ProtocolB: return @"802.11b"; case XPL802_11ProtocolG: return @"802.11g"; case XPL802_11ProtocolN: return @"802.11n"; default: break; } return nil; }
So I have my great little converting function and I end up using it a lot in my code. I notice that my return values are constant NSString references and are always the same based on the protocol that is provided as a parameter to my function. Aha! A prime candidate for a const __attribute__ directive. So I just update my header’s function declaration like so:
FOUNDATION_EXPORT NSString*XPL802_11ProtocolToString(XPL802_11Protocol protocol)__attribute__((const));
And voilà! I have just provided context to any consumer of this function such that they know that the return value is completely based on the provided parameter and won’t change over the course of the process’ life. This change would also provide a performance boost, depending on how often the function is called, since the compiler now knows that it doesn’t actually have to re-execute this function if it already has cached the return value.
Now, let’s say one day I notice the enum value is the character of the protocol and I decide to be clever and change my implementation to something like this:
NSString *XPL802_11ProtocolToString(XPL802_11Protocol protocol) { switch(protocol) { case XPL802_11ProtocolA: case XPL802_11ProtocolB: case XPL802_11ProtocolG: case XPL802_11ProtocolN: return [NSString stringWithFormat:@"802.11%c", protocol]; default: break; } return nil; }
Now since I failed to remove the const attribute, I have just introduced a massive bug that could easily crash my app. Why? Well, the key difference is that the return value of my function is no longer a constant reference. When we were returning hard coded strings before, the compiler stored those strings as persistent memory and those NSStrings effectively had a retain count of infinite. Now that we dynamically generate the string based on the protocol’s char value, we are creating a new string every time - and that means memory that changes. The reference returned in one call to the function won’t actually be the same reference as a subsequent identical call. The problem will rear it’s head when the compiler optimizes subsequent calls to that function to just immediately access what the compiler considers the known return value, which would be the reference returned by the original call. Sadly, that reference has likely been deallocated and potentially reallocated by some other memory allocation by now. This will lead to our application crashing on either a bad memory access or an invalid method call on the object that occupies that reference’s memory. The worst of this is that the optimization that would cause this crash will only happen in builds that are highly optimized. Since debug builds often have optimizations turned down, you can run your app in a debugger forever and never reproduce it, making this bug, like most __attribute__ based bugs, very hard to figure out and fix.
This is bug effectively boils down to treating a function that returns transient memory as const. The same goes for functions or methods that take transient memory as a parameter. Easy enough to remember is that any function returning a pointer return value must return a constant reference to use the const __attribute__ directive and absolutely no const function can have a pointer (including an Objective-C object) as a parameter.
Now this example is merely a precaution for using __attribute__ directives and shouldn’t deter you from using them in your code. If you stick to __attribute__ directives you understand and pay attention to how they are used, you’ll be able to steer clear of these bugs and harness the power __attribute__ directives were meant to provide. Just remember, when in doubt, don’t attribute, because providing the wrong context is worse than providing no context.
To point you in the right direction, below is a compiled list of useful attributes that should be more than enough to improve any developer’s tool belt.
Core __attribute__ directives
__attribute__((availability(…))), NS_AVAILABLE and NS_DEPRECATED
Indicate the availability of an API on the platform
NS_AVAILABLE: Apple macro for attributing an API as available in a given OS release. NS_AVAILABLE_IOS(available_os_version)
NS_DEPRECATED: Apple macro for attributing an API as deprecated in a given OS release.
NS_DEPRECATED_IOS(available_os_version,deprecated_os_version)
FOUNDATION_EXPORT NSString * const MyClassNotification NS_AVAILABLE_IOS(3_0); FOUNDATION_EXPORT NSString * const MyClassNotificationOldKey NS_DEPRECATED_IOS(3_0, 7_0); FOUNDATION_EXPORT NSString * const MyClassNotificaitonNewKey NS_AVAILABLE_IOS(7_0); NS_AVAILABLE_IOS(3_0) @class MyClass : NSObject - (void)oldMethod NS_DEPRECATED_IOS(3_0, 6_0); - (void)newMethod:(out NSError * __autoreleasing *)outError NS_AVAILABLE_IOS(6_0); @end
__attribute__((deprecated(…))) and __attribute__((unavailable(…)))
Indicates that an API is deprecated/unavailable.
__attribute__((deprecated(optional_message)))
__attribute__((unavailable(optional_message)))
In case you don’t want to use the availability attribute for deprecation.
- (void)deprecatedMethod __attribute__((deprecated)); - (void)deprecatedMethodWithMessage __attribute__((deprecated("this method was deprecated in MyApp.app version 5.0.2, use newMethod instead")); - (void)unavailableMethod __attribute__((unavailable)); - (void)unavailableMethodWithMessage __attribute__((unavailable("this method was removed from MyApp.app version 5.3.0, use newMethod instead"));
__attribute__((format(…))) and NS_FORMAT_FUNCTION
Indicates that a function/method contains a format string with format arguments.
__attribute__((format(format_type, format_string_index, first_format_argument_index)))
format_type: one of printf, scant, strftime, strfmon or __NSString__
Reminder: argument indexes when specified in an attribute are 1 based. When it comes to Objective-C methods, remember they are just C functions whose first 2 arguments are the id self argument and the SEL _cmd argument.
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2); void MyLog(MyLogLevel lvl, const char *format, ...) __attribute((format(printf, 2, 3))); // ... - (void)appendFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(3, 4);
__attribute__((sentinel(…))) and NS_REQUIRES_NIL_TERMINATION
Indicates that a function/method requires a nil (NULL) argument, usually used as a delimiter. You can only use this attribute with variadic functions/methods.
__attribute__((sentinel(index))
index: the index offset from the last argument in the variadic list of arguments.
__attribute__((sentinel)) is equivalent to __attribute__((sentinel(0)))
You’ll almost always want to use the NS_REQUIRES_NIL_TERMINATION macro
// Example 1 @interface NSArray - (instancetype)arrayWithObjects:... NS_REQUIRES_NIL_TERMINATION; @end // Example 2 - of course you'd never do this... NSArray *CreateArrayWithObjectsWithLastArgumentIndicatingIfArrayIsMutable(...) __attribute__((sentinel(1))); void foo(id object1, id object2) { NSArray *weirdArray = CreateArrayWithObjectsWithLastArgumentIndicatingIfArrayIsMutable(object1, object2, nil, YES); NSAssert([weirdArrayrespondsToSelector:@selector(addObject:)]); // ... }
__attribute__((const)) and __attribute__((pure))
__attribute__((const)) is used to indicate that the function/method results are entirely dependent on the provided arguments and the function/method does not mutate state.
__attribute__((pure)) is almost the same as its const counterpart, except that the function/method can also take global/static variables into account.
// Example 1: Singleton @interface MySingleton : NSObject + (MySingleton *)sharedInstance __attribute__((const)); @end // Example 2: Function overhead optimization // Get the description of a specified error number const char *StringForErrNo(int errorNumber) __attribute__((const));// strerror(errorNumber) // Get the description of the global errno error number const char *StringForGlobalErrNo(void) __attribute__((pure)); // strerror(errno) void DoStuffWithGlobalErrNo() { NSLog(@"%@ %s", [NSStringstringWithUTF8String:StringForGlobalErrNo()],StringForGlobalErrNo()); printf("%s\n", StringForGlobalErrNo()); printf("%i\n", strlen(StringForGlobalErrNo())); } // will compile as something more like this: void DoStuffWithGlobalErrNo() { const char *__error = StringForGlobalErrNo(); NSLog(@"%@ %s", [NSString stringWithUTF8String:__error], __error); printf("%s\n", __error); printf("%i\n", strlen(__error)); } // which effectively eliminates both 1) the overhead of the function call and 2) the internal execution cost of the function // Example 3: Function execution cost optimization int nthFibonacci(int n) __attribute__((const)); // naive implementation to get the nth fibonacci number without any caching void TestFibonacci() { time_t start = time(NULL); int result1 = nthFibonacci(1000); // execution time of D time_t dur1 = time(NULL) - start; // some large duration D int result2 = nthFibonacci(1000); // execution time of 1 time_t dur2 = time(NULL) - start; // same as dur1, duration D int result3 = nthFibonacci(999); // execution time of ~D time_t dur3 = time(NULL) - start; // duration of 2*D // The __attribute__((const)) directive can effectively eliminate a redundant call to an expensive operation...nice! }
__attribute__((objc_requires_super)) and NS_REQUIRES_SUPER
Indicate that the decorated method must call the super version of it’s implementation if overridden.
@interface MyBaseClass : NSObject - (void)handleStateTransition NS_REQUIRES_SUPER; @end // ... @interface MyConcreteClass : MyBaseClass @end @implementation MyConcreteClass - (void)handleStateTransition { [super handleStateTransition]; // @end
ARC __attribute__ directives
__attribute__((objc_precise_lifetime)) and NS_VALID_UNTIL_END_OF_SCOPE
Indicate that the given variable should be considered valid for the duration of its scope
- (void)foo { NS_VALID_UNTIL_END_OF_SCOPE MyObject *obj = [[MyObject alloc] init]; NSValue *value = [NSValue valueWithPointer:obj]; // do stuff MyObject *objAgain = [value pointerValue]; NSLog(@"%@", objAgain); } /* in ARC, without NS_VALID_UNTIL_END_OF_SCOPE, the compiler will optimize and after the obj pointer is used to create the NSValue the compiler will have no knowledge of the encapsulated use of the object in the NSValue. ARC will release obj and this NSLog line will crash with EXEC_BAD_ACCESS because the reference retrieved from the NSValue and stored in objAgain will now be pointing to the deallocated reference. */
__attribute__((ns_returns_retained)) and NS_RETURNS_RETAINED
Indicates to ARC that the method returns a +1 retain count.
Per Apple: only use this attribute for extraneous circumstances. Use the Objective-C naming convention of prefixing your method with alloc, new, copy, or mutableCopy to achieve the same result without an attribute.
NSString *CreateNewStringWithFormat(NSString *format, ...)NS_FORMAT_FUNCTION(1, 2) NS_RETURNS_RETAINED;
__attribute__((ns_returns_not_retained)) and NS_RETURNS_NOT_RETAINED
Indicates to ARC that the method returns a +0 retain count. Default behavior of all methods and functions in Objective-C.
Per Apple: only use this attribute for extraneous circumstances. Use the Objective-C naming convention of NOT prefixing your method with alloc, new, copy, or mutableCopy to achieve the same result without an attribute.
- (NSString *)newUnretainedStringWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(3, 4) NS_RETURNS_NOT_RETAINED;
__attribute__((objc_returns_inner_pointer)) and NS_RETURNS_INNER_POINTER
Indicates that the method will return a pointer that is only valid for the lifetime of the owner. This will prevent ARC from preemptively releasing an object when the internal pointer is still in use.
@interface NSMutableData : NSData - (void *)mutableBytes NS_RETURNS_INNER_POINTER; @end void Foo(void) { NSMutableData *buffer = [[NSMutableData alloc] initWithLength:8]; char* cBuffer = buffer.mutableBytes; memcpy(cBuffer, "1234567", 8); // crash if NS_RETURNS_INNER_POINTER doesn't decorate the mutableBytes method printf("%s\n", cBuffer); (void)buffer; // this will not save us from a crash if the mutableBytes method isn't decorated with an NS_RETURNS_INNER_POINTER }
__attribute__((ns_consumes_self)) and NS_REPLACES_RECEIVER
Indicates that the provided method can replace the receiver with a different object.
Presumes a +0 retain count (which can be overridden with NS_RETURNS_RETAINED, but if you do that you really need to be asking yourself “what the heck am I doing?”).
@interface NSObject (NSCoderMethods) - (id)awakeAfterUsingCoder:(NSCoder *) NS_REPLACES_RECEIVER; @end
__attribute__((objc_arc_weak_reference_unavailable)) and NS_AUTOMATED_REFCOUNT_WEAK_UNAVAILABLE
Indicates that the decorated class does not support weak referencing
NS_AUTOMATED_REFCOUNT_WEAK_UNAVAILABLE @interface NSHashTable : NSObject *Protocols*/> //... @end
NS_AUTOMATED_REFCOUNT_UNAVAILABLE
Indicates that the decorated API is unavailable in ARC.
- (oneway void)release NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
More __attribute__ directives
__attribute__((objc_root_class)) and NS_ROOT_CLASS
__attribute__((constructor(…))) and __attribute__((destructor(…)))
__attribute__((format_arg(…))) and NS_FORMAT_ARGUMENT
__attribute__((nonnull(…)))
__attribute__((returns_nonnull))
__attribute__((noreturn))
__attribute__((used))
__attribute__((unused))
__attribute__((warn_unused_result))
__attribute__((error(…))) and __attribute__((warning(…)))
in, out and inout
While we’re on the topic of providing context to code we should take the briefest of moments to bring up the Objective-C keywords in, out and inout. These little keywords are used to attribute Objective-C method arguments to provide context on whether the parameter is for input, output or both. These keywords came about with distributed objects along with oneway, byref, and bycopy but, in the spirit of providing context to programmers, these keywords can bridge the gap between the consumer of an API presuming how an argument will behave and knowing how that argument will behave. Consider using inout or out the next time you return a value via an argument and consumers of your API will appreciate it.
in
Indicates that the given argument is used only for input. This is the default behavior for non-pointers and Objective-C objects.
- (void)configureWithRect:(in CGRect *rect) { if (rect) { _configRect = *rect; } [self _innerConfigure]; }
out
Indicates that the given argument is used just for output. This is never a default behavior.
- (void)configure:(out NSError **error) { NSError *theError = [self _configure]; if (error) { *error = theError; } }
inout
Indicates that the given argument is used for both input and output. This is the default behavior for pointers, except for Objective-C objects (which default to in).
- (void)configureRect:(inout CGRect *rect) { if (rect) { if (CGRectIsNull(*rect)) { // where rect acts an "in" argument *rect = CGRectMake(_x, _y, _w, _h); // where rect acts as an "out" argument } } }
__attribute__ directives as a tool
With such a valuable tool available to the C languages, any team can benefit by using these __attribute__ directives to give context in their code. At Twitter, with a very large code base and many engineers developing on it daily, every bit of context that can be provided helps in maintaining a quality code base for reuse. Adding __attribute__ directives to your toolbelt of code comments and good naming conventions will give you a robust toolset for providing indispensable context to your code base. Don’t shy away from adding __attribute__ directives to your next project. Use them, evangelize them and everyone will benefit.
__attribute__ resources
GCC __attribute__ documentation
Clang __attribute__ reference
Clang Objective-C ARC Attributes
NSHipster’s __attribute__ blog post
Did someone say … cookies?
X and its partners use cookies to provide you with a better, safer and
faster service and to support our business. Some cookies are necessary to use
our services, improve our services, and make sure they work properly.
Show more about your choices.