How to use nonnull and nullable Objective-C keywords in block-based API method
Objective CXcodeBlockNullableObjective C Problem Overview
Consider the following method
- (void)methodWithArg:(NSString *)arg1 andArg:(NSString *)arg2 completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler;
With the new nonnull
and nullable
annotation keywords we can enrich it as follows:
- (void)methodWithArg:(nonnull NSString *)arg1 andArg:(nullable NSString *)arg2 completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler;
but we also get this warning:
> Pointer is missing a nullability type specifier (__nonnull or > __nullable)
It refers to the third parameter (the block one).
The documentation doesn't cover with examples how to specify the nullability of block parameters. It states verbatim
> You can use the non-underscored forms nullable and nonnull immediately > after an open parenthesis, as long as the type is a simple object or > block pointer.
I tried putting one of the two keywords for the block (in any position) without any luck. Also tried the underscore prefixed variants (__nonnull
and __nullable
).
Therefore my question is: how can I specify the nullability semantic for block parameters?
Objective C Solutions
Solution 1 - Objective C
This seems to be working
- (void)methodWithArg:(nonnull NSString *)arg1
andArg:(nullable NSString *)arg2 completionHandler:(nullable void (^)
(NSArray * _Nullable results, NSError * _Nonnull error))completionHandler
You need to specify nullability both for the block and its parameters...
EDIT: For more information, see Swift Blog
Solution 2 - Objective C
According to Apple Blog ("Nullability and Objective-C"), you can use
NS_ASSUME_NONNULL_BEGIN
and NS_ASSUME_NONNULL_END
.
Within these regions, any simple pointer type will be assumed to be nonnull
. Then you can just add nullable
for nullable object, which like
NS_ASSUME_NONNULL_BEGIN
@interface MyClass: NSObject
- (void)methodWithArg:(NSString *)arg1 andArg:(nullable NSString *)arg2 completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler;
@end
NS_ASSUME_NONNULL_END
- if error is
NSError **
type, should beNSError * _Nullable * _Nullable
- if object is
id *
type, better useid _Nullable * _Nonnull
, it depends (may be you want a_Nullable id * _Nullable
type). - if object is
NSObject *
type, you need put annotation after pointer, like thisNSObject * _Nullable * _Nonnull
Note
_Nonnull
and _Nullable
should used after pointer or id
(Apple does in the example code AAPLListItem * _Nullable
), but the non-underscored forms nonnull
and nullable
can used after an open parenthesis.
> However, in the common case there’s a much nicer way to write these
> annotations: within method declarations you can use the
> non-underscored forms nullable
and nonnull
immediately after an open
> parenthesis, as long as the type is a simple object or block pointer.
check more in "Nullability and Objective-C"
> For safety, there are a few exceptions to this rule:
>
> * typedef
types don’t usually have an inherent nullability—they can
> easily be either nullable or non-nullable depending on the context.
> Therefore, typedef
types are not assumed to be nonnull
, even within
> audited regions.
> * More complex pointer types like id *
must be
> explicitly annotated. For example, to specify a non-nullable pointer
> to a nullable object reference, use _Nullable id * _Nonnull
.
> * The particular type NSError **
is so often used to return errors via
> method parameters that it is always assumed to be a nullable pointer
> to a nullable NSError
reference.
The _Nullable id * _Nonnull
can be confused, id _Nullable * _Nonnull
is better understanding.
_Nonnull
and _Nullable
should used after pointer or id
(Apple does in the example code AAPLListItem * _Nullable
)
Solution 3 - Objective C
You can also do like this:
- (id __nullable)methodWithArg:(NSString * __nullable)arg1
andArg:(NSString * __nonnull)arg2
completionHandler:(void (^ __nonnull)(NSArray * __nonnull results, NSError * __nullable error))completionHandler;
It only depends which syntax you like more.
Solution 4 - Objective C
To define completions in a header file I did this
typedef void (^PublicEventsHandler) (BOOL success, NSArray * _Nullable publicEvents);
Of course, I agree with the accepted answer.
Solution 5 - Objective C
From apple developer blog: The Core: _Nullable and _Nonnull
> you can use the non-underscored forms > nullable and nonnull immediately after an open parenthesis, as long as > the type is a simple object or block pointer. > > The non-underscored forms are nicer than the underscored ones, but > you’d still need to apply them to every type in your header.
Solution 6 - Objective C
Here is what I have used for the NSError ** case:
-(BOOL) something:(int)number withError:(NSError *__autoreleasing __nullable * __nullable)error;