With Xcode 6.3 there were new annotations introduced for better expressing the intention of API's in Objective-C (and to ensure better Swift support of course). Those annotations were of course nonnull
, nullable
and null_unspecified
.
But with Xcode 7, there is a lot of warnings appearing such as:
Pointer is missing a nullability type specifier (_Nonnull, _Nullable or _Null_unspecified).
In addition to that, Apple uses another type of nullability specifiers, marking their C code (source):
CFArrayRef __nonnull CFArrayCreate(
CFAllocatorRef __nullable allocator, const void * __nonnull * __nullable values, CFIndex numValues, const CFArrayCallBacks * __nullable callBacks);
So, to sum up, we now have these 3 different nullability annotations:
nonnull
, nullable
, null_unspecified
_Nonnull
, _Nullable
, _Null_unspecified
__nonnull
, __nullable
, __null_unspecified
Even though I know why and where to use which annotation, I'm getting slightly confused by which type of annotations should I use, where and why. This is what I could gather:
nonnull
, nullable
, null_unspecified
.nonnull
, nullable
, null_unspecified
.__nonnull
, __nullable
, __null_unspecified
._Nonnull
, _Nullable
, _Null_unspecified
.But I'm still confused as to why we have so many annotations that basically do the same thing.
So my question is:
What is exact difference between those annotations, how to correctly place them and why?
From the clang
documentation:
The nullability (type) qualifiers express whether a value of a given pointer type can be null (the
_Nullable
qualifier), doesn’t have a defined meaning for null (the_Nonnull
qualifier), or for which the purpose of null is unclear (the_Null_unspecified
qualifier). Because nullability qualifiers are expressed within the type system, they are more general than thenonnull
andreturns_nonnull
attributes, allowing one to express (for example) a nullable pointer to an array of nonnull pointers. Nullability qualifiers are written to the right of the pointer to which they apply.
, and
In Objective-C, there is an alternate spelling for the nullability qualifiers that can be used in Objective-C methods and properties using context-sensitive, non-underscored keywords
So for method returns and parameters you can use the
the double-underscored versions __nonnull
/__nullable
/__null_unspecified
instead of either the single-underscored ones, or instead of the non-underscored ones. The difference is that the single and double underscored ones need to be placed after the type definition, while the non-underscored ones need to be placed before the type definition.
Thus, the following declarations are equivalent and are correct:
- (nullable NSNumber *)result
- (NSNumber * __nullable)result
- (NSNumber * _Nullable)result
For parameters:
- (void)doSomethingWithString:(nullable NSString *)str
- (void)doSomethingWithString:(NSString * _Nullable)str
- (void)doSomethingWithString:(NSString * __nullable)str
For properties:
@property(nullable) NSNumber *status
@property NSNumber *__nullable status
@property NSNumber * _Nullable status
Things however complicate when double pointers or blocks returning something different than void are involved, as the non-underscore ones are not allowed here:
- (void)compute:(NSError * _Nullable * _Nullable)error
- (void)compute:(NSError * __nullable * _Null_unspecified)error;
// and all other combinations
Similar with methods that accept blocks as parameters, please note that the nonnull
/nullable
qualifier applies to the block, and not its return type, thus the following are equivalent:
- (void)executeWithCompletion:(nullable void (^)())handler
- (void)executeWithCompletion:(void (^ _Nullable)())handler
- (void)executeWithCompletion:(void (^ __nullable)())handler
If the block has a return value, then you're forced into one of the underscore versions:
- (void)convertObject:(nullable id __nonnull (^)(nullable id obj))handler
- (void)convertObject:(id __nonnull (^ _Nullable)())handler
- (void)convertObject:(id _Nonnull (^ __nullable)())handler
// the method accepts a nullable block that returns a nonnull value
// there are some more combinations here, you get the idea
As conclusion, you can use either ones, as long as the compiler can determine the item to assign the qualifier to.