为啥 Objective-C API 会返回隐式展开的可选项?
Posted
技术标签:
【中文标题】为啥 Objective-C API 会返回隐式展开的可选项?【英文标题】:Why do Objective-C APIs return implicitly unwrapped optionals?为什么 Objective-C API 会返回隐式展开的可选项? 【发布时间】:2014-07-14 00:48:53 【问题描述】:我对此感到很困惑。如果我们以UITableView
中的cellForRowAtIndexPath:
方法为例,它的方法签名是:
func cellForRowAtIndexPath(_ indexPath: NSIndexPath!) -> UITableViewCell!
而它的返回值为:
表示表格单元格的对象,如果单元格不可见或 indexPath 超出范围,则为 nil。
这听起来是使用标准可选项的完美理由。事实上,由于在 Objective-C 中所有基于指针的类型都可以是 nil...所有 Objective-C 指针类型都应该作为标准选项导入似乎是有道理的。
我从 WWDC 的谈话中知道,他们说对于隐式展开的选项:
可以显式测试 nil 可以直接访问底层值的属性/方法 可以隐式转换为其基础值来自苹果的Using Swift with Cocoa and Objective-C:
当您访问这种可选类型中的值而没有先安全地解包它时,隐式解包的可选项会检查该值是否丢失。如果缺少该值,则会发生运行时错误。
因此,他们决定将其导入为声明 this 永远不应该为 nil 的东西,而不是将可能的 nil 值作为可选值导入 Swift ......但可能吗?听起来他们这样做完全否定了 Swift 中用于 Objective-C API 的可选类型的安全性。我似乎缺少什么?
他们认为运行时错误更好,而不是给出编译时错误或警告?这非常令人困惑。
考虑到似乎没有任何东西可以回答我所看到的这个问题...我认为这对其他人来说是显而易见的,我只是没有看到但是...为什么会这样?
真的只是为了让人们在 Swift 中使用 Objective-C API 时免于使用 if let
或可选链接,还是其他方式?
【问题讨论】:
【参考方案1】:当你在 Swift 中创建一个隐式解包的可选项时,这并不意味着它总是非 nil:这意味着你告诉编译器当你访问它们的属性时,你期望对象是非nil
。您引用的对象可以显式检查nil
;将其设置为 nil
也不会导致异常,除非您在此之后尝试访问其任何属性。
当 Apple 对参数使用隐式解包选项时
func tableView(_ tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
功能,它们让您节省了一些额外的if
- let
。在这种情况下,他们知道他们永远不会向您传递nil
;在其他情况下,他们不知道,他们希望您nil
-检查对象。
它们也允许您返回nil
。由他们来检查nil
的结果,当然,除非您决定自己调用该函数。虽然我想不出从您自己的代码中调用cellForRowAtIndexPath
的正当理由,但如果您确实进行了调用,您有责任检查nil
的返回值。
如果您考虑将参数设置为UITableView?
和NSIndexPath?
的替代方法,则所有实现都必须在tableView
和indexPath
之后使用感叹号,或者使用if
- let
成语。与此选择相比,隐式展开类型看起来是更好的选择。
【讨论】:
但是函数的返回值不能是 UITableViewCell 吗? ?为什么不让它成为一个普通的可选而不是一个隐式展开的呢? @NickH 是的,这也可以,但是对于使用他们的 API 的程序员来说,它会变得不那么方便。当UITableView
回调数据源时,您可以放心地假设这两个参数都不是nil
。它们是可选的唯一原因是 Objective-C 的互操作性:在原生 Swift API 中,这两个参数可能都是非可选的。
“他们实际上向您承诺,他们传递给您的 tableView 和 indexPath 都不会是 nil” 不。没有这样的“承诺”。 Objective-C API 中的所有 对象指针类型自动 成为隐式解包的可选项;不涉及情报。在某些情况下,其中许多 API 可以(实际上应该)返回 nil
。
@newacct 因此我感到困惑,这似乎是一个有人想要使用普通可选的地方。在使用 Objective-C API 时,隐式可选只是让编写 Swift 更容易,更像是 Objective-C,但它似乎否定了普通可选的安全性。
@NickH:这是绝对安全和便利之间的权衡。因为它可能是nil
,所以它必须是可选的。当您确定它不是nil
(如果您错了,它会引发错误)时,隐式展开使您更方便,就像您在任何地方显式展开它一样。如果您不确定它不是nil
,您仍然可以使用可选绑定和可选链接。所以基本上你程序员可以选择合适的方式来使用它。这样,Apple 就不必在整个 API 中经过并明确注释可空/不可空。【参考方案2】:
以下是lists.swift.org 上swift-users 中的Greg Parker's answer:
作为隐式解包的可选导入是一种可用性 妥协。大多数 Objective-C 指针实际上从来都不是 nil。如果指针为nil,并且作者没有检查,那么流程 故意停下来。这并不比你得到的行为更糟糕 编写 Objective-C 代码。 IUO 进口旨在作为权宜之计。我n 从长远来看,每个 Objective-C 接口都应该被显式注释,以便 Swift 可以更精确地导入它们。您可以在自己的代码中使用 NS_ASSUME_NONNULL_BEGIN/END 在你的头文件中。每个未注释的 这些标记内的对象指针是非空的。
【讨论】:
以上是关于为啥 Objective-C API 会返回隐式展开的可选项?的主要内容,如果未能解决你的问题,请参考以下文章
为啥隐式符号到字符串的转换会导致 JavaScript 中的 TypeError?