为什么Objective-C API会隐式返回未包装的选项?

时间:2022-09-07 11:16:17

I am rather perplexed by this. If we take the method cellForRowAtIndexPath: in UITableView for example, it's method signature is:

我对此感到困惑。例如,如果我们在U​​ITableView中使用方法cellForRowAtIndexPath:它的方法签名是:

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

And its return value is:

它的返回值是:

An object representing a cell of the table or nil if the cell is not visible or indexPath is out of range.

表示表格单元格的对象,如果单元格不可见或indexPath超出范围,则为nil。

That sounds like the perfect reason to use a standard optional. In fact, since all pointer based types in Objective-C can be nil... it seems to make sense that all Objective-C pointer types should be imported as standard optionals.

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

I know from the WWDC talk that they say that for implicitly unwrapped optionals:

我从WWDC的演讲中得知,他们说这是隐含的未解决的选项:

  • Can be tested explicitly for nil
  • 可以明确测试为零
  • Can directly access properties/methods of the underlying value
  • 可以直接访问基础值的属性/方法
  • Can be implicitly converted to its underlying value
  • 可以隐式转换为其基础值

And from Apple's Using Swift with Cocoa and Objective-C:

从Apple的使用Swift与Cocoa和Objective-C:

When you access the value in this kind of optional type without safely unwrapping it first, the implicitly unwrapped optional checks whether the value is missing. If the value is missing, a runtime error occurs.

当您以这种可选类型访问该值而不首先安全地展开它时,隐式解包的可选项将检查该值是否缺失。如果缺少该值,则会发生运行时错误。

So, instead of importing a possibly nil value into Swift as an optional, they decided to import it as something that states that this should never be nil... but could be? It sounds like they completely negated the safety of the optional type in Swift for Objective-C APIs by doing this. What do I seem to be missing?

所以,不是将可能的nil值作为可选项导入到Swift中,而是决定将其导入为一个声明永远不会为零的东西......但可能是?听起来他们通过这样做完全否定了Swift for Objective-C API中可选类型的安全性。我似乎缺少什么?

Instead of giving a compile time error or warning, they decided a runtime error was better? This is very confusing.

他们没有给出编译时错误或警告,而是认为运行时错误更好?这非常令人困惑。

Considering that nothing seems to answer this question that I have seen... I am thinking it is something obvious to everybody else that I am just not seeing but... Why is it like this?

考虑到似乎没有任何东西可以回答我所看到的这个问题......我认为对于其他所有人来说,我只是没有看到,但是......为什么会这样?

Is it really just to save people from using if let or optional chaining when they use Objective-C APIs in Swift, or something more?

是否真的只是为了节省人们在使用Swift中的Objective-C API时使用let或可选链接,还是更多?

2 个解决方案

#1


5  

When you make an implicitly unwrapped optional in Swift, it does not mean that it is always going to be non-nil: all it means is that you tell the compiler that when you access their properties, you expect the object to be non-nil. The object that you reference can be explicitly checked for nil; setting it to nil will not cause an exception either, unless you try to access any of its properties after that.

当你在Swift中创建一个隐式解包的可选项时,并不意味着它总是非nil:它意味着你告诉编译器当你访问它们的属性时,你希望该对象是非零的。您引用的对象可以显式检查为nil;将其设置为nil也不会导致异常,除非您在此之后尝试访问其任何属性。

When Apple used implicitly unwrapped optionals for the parameters of

当Apple使用隐式解包的选项时参数

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

function, they let you save on a few extra if - let. In this case, they know that they never pass you a nil; in other cases, they do not know it, and they expect you to nil-check the object.

功能,他们让你节省一些额外的if - let。在这种情况下,他们知道他们永远不会给你一个零;在其他情况下,他们不知道,他们希望你检查对象。

They allow you to return nil, too. It is up to them to check the results for nil, unless, of course, you decide to call that function yourself. Although I cannot think of a valid reason to call cellForRowAtIndexPath from your own code, if you do make a call, it would be your responsibility to check the return value for nil.

它们也允许你返回nil。他们应该检查nil的结果,除非您决定自己调用该函数。虽然我无法想到从您自己的代码调用cellForRowAtIndexPath的正当理由,但如果您确实进行了调用,那么检查nil的返回值是您的责任。

If you consider an alternative of making the parameters UITableView? and NSIndexPath? instead, all implementations would have to either use an exclamation point after tableView and indexPath, or use the if - let idiom. Compared to this choice, implicitly unwrapped types look like a better choice.

如果你考虑制作参数UITableView的替代方案?和NSIndexPath?相反,所有实现都必须在tableView和indexPath之后使用感叹号,或者使用if-let惯用法。与此选择相比,隐式展开的类型看起来是更好的选择。

#2


2  

The following was Greg Parker's answer in swift-users at lists.swift.org:

以下是Greg Parker在lists.swift.org上的swift-users中的回答:

Importing as implicitly-unwrapped optional is a usability compromise. Most Objective-C pointers are never actually nil. If a pointer is nil, and the author didn't check, then the process deliberately halts. This is no worse than the behavior you get when writing Objective-C code. IUO import is intended to be a stopgap. In the long term every Objective-C interface ought to be explicitly annotated so that Swift can import them more precisely. In your own code you can use NS_ASSUME_NONNULL_BEGIN/END in your header files. Every un-annotated object pointer inside those markers is nonnull.

导入为隐式展开的可选项是可用性折衷。大多数Objective-C指针实际上都不是零。如果指针为nil且作者未检查,则该过程故意停止。这并不比编写Objective-C代码时的行为更糟​​糕。 IUO导入旨在成为权宜之计。从长远来看,每个Objective-C接口都应该明确注释,以便Swift可以更精确地导入它们。在您自己的代码中,您可以在头文件中使用NS_ASSUME_NONNULL_BEGIN / END。这些标记内的每个未注释的对象指针都是非空的。

#1


5  

When you make an implicitly unwrapped optional in Swift, it does not mean that it is always going to be non-nil: all it means is that you tell the compiler that when you access their properties, you expect the object to be non-nil. The object that you reference can be explicitly checked for nil; setting it to nil will not cause an exception either, unless you try to access any of its properties after that.

当你在Swift中创建一个隐式解包的可选项时,并不意味着它总是非nil:它意味着你告诉编译器当你访问它们的属性时,你希望该对象是非零的。您引用的对象可以显式检查为nil;将其设置为nil也不会导致异常,除非您在此之后尝试访问其任何属性。

When Apple used implicitly unwrapped optionals for the parameters of

当Apple使用隐式解包的选项时参数

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

function, they let you save on a few extra if - let. In this case, they know that they never pass you a nil; in other cases, they do not know it, and they expect you to nil-check the object.

功能,他们让你节省一些额外的if - let。在这种情况下,他们知道他们永远不会给你一个零;在其他情况下,他们不知道,他们希望你检查对象。

They allow you to return nil, too. It is up to them to check the results for nil, unless, of course, you decide to call that function yourself. Although I cannot think of a valid reason to call cellForRowAtIndexPath from your own code, if you do make a call, it would be your responsibility to check the return value for nil.

它们也允许你返回nil。他们应该检查nil的结果,除非您决定自己调用该函数。虽然我无法想到从您自己的代码调用cellForRowAtIndexPath的正当理由,但如果您确实进行了调用,那么检查nil的返回值是您的责任。

If you consider an alternative of making the parameters UITableView? and NSIndexPath? instead, all implementations would have to either use an exclamation point after tableView and indexPath, or use the if - let idiom. Compared to this choice, implicitly unwrapped types look like a better choice.

如果你考虑制作参数UITableView的替代方案?和NSIndexPath?相反,所有实现都必须在tableView和indexPath之后使用感叹号,或者使用if-let惯用法。与此选择相比,隐式展开的类型看起来是更好的选择。

#2


2  

The following was Greg Parker's answer in swift-users at lists.swift.org:

以下是Greg Parker在lists.swift.org上的swift-users中的回答:

Importing as implicitly-unwrapped optional is a usability compromise. Most Objective-C pointers are never actually nil. If a pointer is nil, and the author didn't check, then the process deliberately halts. This is no worse than the behavior you get when writing Objective-C code. IUO import is intended to be a stopgap. In the long term every Objective-C interface ought to be explicitly annotated so that Swift can import them more precisely. In your own code you can use NS_ASSUME_NONNULL_BEGIN/END in your header files. Every un-annotated object pointer inside those markers is nonnull.

导入为隐式展开的可选项是可用性折衷。大多数Objective-C指针实际上都不是零。如果指针为nil且作者未检查,则该过程故意停止。这并不比编写Objective-C代码时的行为更糟​​糕。 IUO导入旨在成为权宜之计。从长远来看,每个Objective-C接口都应该明确注释,以便Swift可以更精确地导入它们。在您自己的代码中,您可以在头文件中使用NS_ASSUME_NONNULL_BEGIN / END。这些标记内的每个未注释的对象指针都是非空的。