为什么Objective-C APIs会返回隐式解包可选项?

16

我对此感到困惑。以UITableView中的cellForRowAtIndexPath:方法为例,其方法签名如下:

func cellForRowAtIndexPath(_ indexPath: NSIndexPath!) -> UITableViewCell!

其返回值为:

一个代表表格单元的对象,如果单元格不可见或indexPath越界,则为nil。

这听起来像使用标准可选项的完美理由。事实上,由于Objective-C中所有基于指针的类型都可以是nil...因此,似乎将所有Objective-C指针类型导入为标准可选项是有意义的。

我知道从WWDC演讲中他们说对于隐式展开的可选项:

  • 可以明确测试是否为nil
  • 可以直接访问底层值的属性/方法
  • 可以隐式转换为其底层值

来自苹果的“使用Swift与Cocoa和Objective-C”

当您在没有安全解包它之前访问此类可选类型中的值时,隐式展开的可选项将检查该值是否丢失。如果缺少该值,将出现运行时错误。

因此,他们决定将可能为nil的值导入Swift作为某些声明此永远不会是nil的东西...但可能是?听起来,他们通过这种方式完全取消了Swift可选类型在Objective-C API中的安全性。我错过了什么吗?

他们为什么要这样做,而不是给出编译时错误或警告呢?这很困惑。

考虑到我没有看到任何回答这个问题的东西...我认为这对其他人来说显然是一个明显的问题,而我只是没有看到...为什么会这样呢?

这真的只是为了节省人们在Swift中使用Objective-C API时使用 if let 或可选链接的时间,还是其他原因?

2个回答

6
当你在Swift中创建一个隐式解包可选项时,这并不意味着它总是非空的:它只是告诉编译器,当你访问它的属性时,你期望对象是非nil的。你引用的对象可以明确地检查是否为nil;将其设置为nil也不会导致异常,除非在此之后尝试访问其任何属性。
当Apple使用隐式解包可选项作为参数时...
func tableView(_ tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!

这个函数可以让你省略一些额外的if - let。在这种情况下,他们知道他们永远不会传递给你一个nil;在其他情况下,他们不知道,他们期望你检查对象是否为nil

它们也允许你返回nil。除非你自己调用该函数,否则由它们来检查结果是否为nil。虽然我想不出一个调用cellForRowAtIndexPath的有效理由,但如果你确实进行了调用,你有责任检查返回值是否为nil

如果你考虑将参数改为UITableView?NSIndexPath?,那么所有的实现都必须在tableViewindexPath后使用感叹号,或者使用if - let习语。与这个选择相比,隐式解包类型看起来是更好的选择。


3
他们并没有向您承诺过传递给您的tableView和indexPath不会为空。实际上,在被导入到Swift中的Objective-C APIs中,所有对象指针类型都会自动变为隐式可选项,这并不需要智能处理。许多API在某些情况下可以(事实上应该)返回nil - newacct
@newacct 因此我感到困惑,这似乎是某人想要使用普通可选项的地方。隐式可选项使得在使用Objective-C API时编写Swift更加容易,但它似乎抵消了普通可选项的安全性。 - NickH
@dasblinkenlight,我对返回值是隐式解包可选项而不是普通可选项更加困惑。如果我们考虑一些人更有可能调用的东西,比如NSDate上的laterDate(func laterDate(_ anotherDate: NSDate!) -> NSDate!),它会返回一个隐式解包的NSDate。为什么不只是标准的可选NSDate,而是一个隐式解包的呢? - NickH
2
@NickH:这是绝对安全和方便之间的权衡。由于它可能是nil,因此必须是可选的。隐式解包使您在确定它不是nil的情况下更加方便(如果您错了,它会抛出错误),就像在任何地方显式解包一样。如果您不确定它是否为nil,仍然可以使用可选绑定和可选链接。因此,程序员可以选择适当的使用方式。这样,苹果就不必在整个API中显式注释可空/非可空。 - newacct
有没有人发现我们如何“注释”我们的自定义Objective-C API以指定它们应该如何映射到Swift? - Klaas
显示剩余3条评论

5
以下是 swift-users 邮件列表中 Greg Parker的答案:

将导入声明为隐式展开的可选类型是一种可用性妥协。大多数Objective-C指针实际上从未是nil。如果一个指针是nil,而作者没有检查,那么进程会故意停止。这并不比编写Objective-C代码时获得的行为更糟糕。 IUO导入旨在作为一种临时措施。 长期来看,每个Objective-C接口都应该明确注释,以便Swift可以更精确地导入它们。 在您自己的代码中,您可以在头文件中使用NS_ASSUME_NONNULL_BEGIN/END。这些标记内的每个未注释的对象指针都是非null的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接