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

Posted

技术标签:

【中文标题】如何在基于块的 API 方法中使用非空和可为空的 Objective-C 关键字【英文标题】:How to use nonnull and nullable Objective-C keywords in block-based API method 【发布时间】:2015-06-21 06:39:55 【问题描述】:

考虑以下方法

- (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;

但我们也收到此警告:

指针缺少可空性类型说明符(__nonnull 或 __nullable)

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

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

您可以立即使用非下划线形式 nullable 和 nonnull 在左括号之后,只要类型是简单对象或 块指针。

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

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

【问题讨论】:

【参考方案1】:

这似乎有效

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

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

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

【讨论】:

NSError ** 类型如何工作?我似乎无法让编译器满意。 据swift博客: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.developer.apple.com/swift/blog/?id=25 @duhanebel 答案在***.com/questions/33198597/…中给出:(NSError * _Nullable *_Nullable) 错误【参考方案2】:

这是我用于 NSError ** 案例的内容:

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

【讨论】:

正如Apple 所说,对于NSError **,您无需指定其可空性。【参考方案3】:

为了在头文件中定义完成,我这样做了

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

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

【讨论】:

【参考方案4】:

来自苹果developer blog:核心:_Nullable 和 _Nonnull

您可以使用不带下划线的形式 可空和非空紧跟在左括号之后,只要 类型是一个简单的对象或块指针。

不带下划线的形式比带下划线的更好,但是 您仍然需要将它们应用于标题中的每种类型

【讨论】:

是的,但非下划线(更好)的在块声明中不起作用【参考方案5】:

你也可以这样做:

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

这仅取决于您更喜欢哪种语法。

【讨论】:

您能详细说明一下您定义的含义吗?我是否正确地认为此方法需要代码块,并且不能为零?我试图在最后一个括号 NSError * __nullable error) _Nonnull )completionHandler; 之前添加 _Nonnull并得到了一个令人讨厌的编译器错误。【参考方案6】:

根据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 可以在左括号之后使用。

但是,在常见的情况下,有一种更好的方法来编写这些 注释:在方法声明中,您可以使用 非下划线形式 nullablenonnull 在打开之后立即 括号,只要类型是简单的对象或块指针。

在"Nullability and Objective-C"查看更多信息

为了安全起见,这条规则有一些例外:

typedef 类型通常不具有固有的可空性——它们可以 根据上下文很容易可以为空或不可为空。 因此,typedef 类型不假定为 nonnull,即使在 被审计的地区。 必须使用更复杂的指针类型,例如 id * 明确注释。例如,指定一个不可为空的指针 对于可为空的对象引用,请使用 _Nullable id * _Nonnull。 特定类型NSError ** 经常用于通过以下方式返回错误 方法参数,它总是被假定为一个可为空的指针 到一个可以为空的 NSError 引用。

_Nullable id * _Nonnull 可以混淆,id _Nullable * _Nonnull 更好理解。

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

【讨论】:

对于弱属性,应用_Nullable。

以上是关于如何在基于块的 API 方法中使用非空和可为空的 Objective-C 关键字的主要内容,如果未能解决你的问题,请参考以下文章

XmlConvert 和可为空的结果?

Laravel 验证中有时和可为空的区别

XmlSerializer 和可为空的属性

JPA 可嵌入 PK 和可为空的字段

XmlCodeExporter 和可为空的类型

将 XDocument.Descendants 与合并运算符一起使用??和可为空的类型