在Apple方法中隐式解包可选

时间:2014-09-24 14:47:31

标签: ios uiviewcontroller swift

我正在尝试从Objective-C切换到Swift。 我不明白声明函数返回AnyObject!而不是AnyObject?的重点。

例如:

func instantiateViewControllerWithIdentifier(identifier: String) -> AnyObject!

为什么这个方法返回一个隐式解包的可选项而不是一个简单的可选项? 我得到了AnyObject部分,但是如果它可能是nil,那么允许我们避免使用!来解包可选项是什么意思? (因此崩溃应用程序,即使它不太可能)

我错过了什么?

是否只是一种方便的方法来使用此方法的返回值而不必使用!或者还有其他我看不到的东西?在这种情况下,如果程序返回nil,程序是否注定要崩溃?

听起来对我说:

  • AnyObject表示100%有机会返回某些内容
  • AnyObject?表示有50%的机会返回nil,你应该总是检查nil
  • AnyObject!表示有99%的机会返回不为零的内容,不需要检查nil

另外,我假设有一些与Objective-C / NSObject类的链接,但无法弄清楚是什么......

谢谢

3 个答案:

答案 0 :(得分:3)

Cocoa API中隐式解包的选项(IUO)的频率是从ObjC将这些API导入Swift的工件。

在ObjC中,对对象的所有引用都只是内存指针。就语言/编译器所知,任何指针都是nil在技术上总是可行的。实际上,采用对象参数的方法可以实现为永不允许传递nil,或者返回对象的方法永远不会返回nil。但ObjC没有提供在API的头声明中断言的方法,因此编译器必须假设nil指针对任何对象引用都有效。

在Swift中,对象引用不仅仅是指向内存的指针;相反,他们是一种语言结构。通过允许我们断言给定引用总是指向一个对象,或者使用选项来允许这种可能性并要求它被考虑,这获得了额外的安全性。

但是,如果引用很可能永远不会为零,那么处理可选的展开可能会很痛苦。所以我们也有IUO类型,它允许你使用选项而不检查它们是否为nil(风险自负)。

因为编译器不知道ObjC API中的哪些引用是否安全可空,所以它必须使用某种可选类型导入ObjC API(Apple或您自己的)中的所有对象引用。无论出于何种原因,Apple选择将IUO用于所有导入的API。可能是因为许多(但很重要的是不是全部!)导入的API实际上是不可为空的,或者可能是因为它允许您像在ObjC中那样编写链接代码(self.view.scene.rootNode等)。

在某些API(包括Foundation和UIKit的大部分)中,Apple已手动审核导入的声明,以使用完整选项(例如UIView?)或非可选引用(UIView)而非IUO在语义上合适。但并非所有API都已经过审核,有些仍然使用IUO,因为这仍然是那些API最合适的事情。 (例如,他们总是将id导入AnyObject!非常一致。)

所以,回到你的问题中的表格:最好不要考虑概率。处理返回...的API时

  • AnyObject:总是有一个值,不能为零。
  • AnyObject?:可能是零,必须检查是否
  • AnyObject!:无保证。阅读该API的标题,文档或源代码(如果有),以确定它是否真的可以在您的情况下为零,并相应地对待它。或者只是假设它可以是零并且防御性地编码。

如果是instantiateViewControllerWithIdentifier,您可以将两个选项称为同等有效:

  1. 假设您保证永远不会为零,因为您知道您在故事板中放置了具有该标识符的视图控制器,并且您知道它是什么类。

    let vc = storyboard.instantiateViewControllerWithIdentifier("vc") as MyViewController
    // do stuff with vc
    
  2. 假设您的故事板将来可能会发生变化,并在您遇到问题时设置有意义的调试错误。

    if let vc = storyboard.instantiateViewControllerWithIdentifier("vc") as? MyViewController {
        // do something with vc
    } else {
        fatalError("missing expected storyboard content")
    }
    

答案 1 :(得分:1)

AnyObject表示不能nil

AnyObject?AnyObject!都表示他们可以nil。它们之间没有“概率”差异。它们之间的唯一区别在于AnyObject,为方便起见,您不必明确强行拆开它。如果你想安全地使用它们,你可以使用可选的绑定和可选的链接。

Objective-C中的对象指针类型默认导入到Swift中作为隐式解包的选项(!)。那是因为所有Objective-C指针都可能是nil(无法从Objective-C声明中自动判断特定对象指针是否可以nil),但可能在一个特定的API,它不能nil,所以如果是这种情况,他们不想让你明显解包。

Apple一直在“审核”其API以手动添加每个对象指针类型是否为nil的信息,因此随着时间的推移,您会看到API中的AnyObject!更改为{{ 1}}或AnyObject。这个过程需要大量的人力,而且进展缓慢。

答案 2 :(得分:-1)

关于以下函数声明:

#1) func instantiateViewControllerWithIdentifier(identifier: String) -> AnyObject
#2) func instantiateViewControllerWithIdentifier(identifier: String) -> AnyObject!
#3) func instantiateViewControllerWithIdentifier(identifier: String) -> AnyObject?

#2和#3在逻辑上是相同的,但是,根据 The Swift Programming Language iBook的第40页,答案就像Rickster的答案一样,是一个语义问题

AnyObject!和AnyObject?在幕后处理相同的(它们最初设置为nil并且在任何时候都可以是nil),但它是意图曾经AnyObject!已设置,不应再指向nil

这允许人们避免使用强制可选的解包语法(!)并简单地使用隐式解包的选项(用!声明的变量)访问指定变量中包含的值。

书中的一个例子:

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation mark

适用于上述内容:

func instantiateViewControllerWithIdentifier(identifier: String) -> AnyObject! {
    return nil
}

let optionalVC: AnyObject! = instantiateViewControllerWithIdentifier("myVC")
self.viewController.presentViewController(optionalVC as UIViewController, animated: true, completion: nil)

// The above is perfectly valid at compile time, but will fail at runtime:
// fatal error: unexpectedly found nil while unwrapping an Optional value

因此,一个将返回值声明为AnyObject的函数! 可以返回nil但是,应该避免这样做以避免违反隐式解包的选项 暗示合同