如何在基于块的 API 方法中使用非空和可为空的 Objective-C 关键字

How to use nonnull and nullable Objective-C keywords in block-based API method

考虑以下方法

- (void)methodWithArg:(NSString *)arg1 andArg:(NSString *)arg2 completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler;

有了新的 nonnullnullable annotation keywords 我们可以如下丰富它:

- (void)methodWithArg:(nonnull NSString *)arg1 andArg:(nullable NSString *)arg2 completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler;

但我们也收到此警告:

Pointer is missing a nullability type specifier (__nonnull or __nullable)

指的是第三个参数(块一)

documentation 没有举例说明如何指定块参数的可空性。它逐字说明

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.

我尝试为块(在任何位置)放置两个关键字之一,但没有成功。还尝试了下划线前缀变体(__nonnull__nullable)。

因此我的问题是:如何为块参数指定可空性语义?

这似乎有效

- (void)methodWithArg:(nonnull NSString *)arg1 
  andArg:(nullable NSString *)arg2 completionHandler:(nullable void (^)
  (NSArray * _Nullable results, NSError * _Nonnull error))completionHandler

您需要为块及其参数指定可空性...

编辑:有关详细信息,请参阅 Swift Blog

这是我在 NSError ** 案例中使用的内容:

-(BOOL) something:(int)number withError:(NSError *__autoreleasing  __nullable * __nullable)error;

为了在头文件中定义补全,我这样做了

typedef void (^PublicEventsHandler) (BOOL success, NSArray * _Nullable publicEvents);

当然,我同意接受的答案。

来自 apple developer blog:核心:_Nullable 和 _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.

你也可以这样做:

- (id __nullable)methodWithArg:(NSString * __nullable)arg1
                        andArg:(NSString * __nonnull)arg2
             completionHandler:(void (^ __nonnull)(NSArray * __nonnull results, NSError * __nullable error))completionHandler;

只看你更喜欢哪种语法。

根据Apple Blog ("Nullability and Objective-C"), 你可以使用

NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END.

在这些区域内,任何简单的指针类型都将被假定为 nonnull。然后你可以为可为空的对象添加 nullable,比如

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
  • 如果errorNSError **类型,应该是NSError * _Nullable * _Nullable
  • 如果对象是 id * 类型,最好使用 id _Nullable * _Nonnull,这取决于(可能你想要 _Nullable id * _Nullable 类型)。
  • 如果对象是NSObject *类型,需要在指针后面加上注解,像这样NSObject * _Nullable * _Nonnull

备注

_Nonnull_Nullable 应在指针或 id 之后使用(Apple 在示例代码 AAPLListItem * _Nullable 中这样做),但非下划线形式 nonnullnullable 可以在左括号后使用。

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.

"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.

_Nullable id * _Nonnull容易混淆,id _Nullable * _Nonnull更好理解。

_Nonnull_Nullable 应在指针或 id 之后使用(Apple 在示例代码中使用 AAPLListItem * _Nullable