UIViewController init 方法中的隐式解包选项

Posted

技术标签:

【中文标题】UIViewController init 方法中的隐式解包选项【英文标题】:Implicitly Unwrapped Optionals in UIViewController init method 【发布时间】:2014-06-03 13:25:32 【问题描述】:

正如文档所说“您确定可选项确实包含一个值,您可以通过添加感叹号 (!) 来访问其基础值”

那么为什么要使用 UIViewController 的 init 方法

init(nibName nibName: String!,bundle nibBundle: NSBundle!)

并告诉我“如果指定 nil,则 nibName 属性设置为 nil。

为什么不改用init(nibName nibName: String?,bundle nibBundle: NSBundle?)

我对此感到很困惑。

【问题讨论】:

如果您没有任何 Nib,只需调用 [aViewController alloc] init]。 @iAn 我只是想知道为什么要使用!不是 ?迅速 Why create "Implicitly Unwrapped Optionals"?的可能重复 @Wayne 说 swift 还为时过早,需要分析思考。不必要地它会产生更多的问题,除了原来的;) 也许这意味着如果它们为 nil,你可以不考虑这些论点?仅当 nibName 和 nibBundle 为非 nil 时才传递它们。 IE。呼叫init() 【参考方案1】:

据我了解,? 用于表示可选变量。 ! 用于访问可选变量的值。文件说

“一旦您确定可选项确实包含一个值,您就可以通过添加感叹号 (!) 来访问其基础值”

这意味着只有当你确定它有一个非零值时才使用!,否则它会抛出错误。 这就像强制可选变量具有值

那么关于你的问题

init(nibName nibName: String!,bundle nibBundle: NSBundle!)

这里的 init 函数强制调用者传递 nibNamenibBundle 的值。它不能为零。 ! 用于确保参数具有非零值

注意:如果我错了,请纠正我,我只是学习:)

【讨论】:

感谢您的回复,但是,文档允许您将 nil 传递给 nibName 和 nibBundle,您可以从 Objective C 的 init 方法中引用,并且文档还说“如果您指定 nil,则 nibName 属性设置为 nil 。” 如果将 nil 作为 nibName 传递会发生什么?你试过了吗 你可以建立成功:)【参考方案2】:

! 是纯语法糖。该变量仍然是可选的;您可以将其用作普通可选,传递nil 等。这只是为了简化为nibName 而不是使用完整的nibName!

来源——

“这些类型的可选项被定义为隐式展开的可选项。通过在要设为可选的类型之后放置感叹号 (String!) 而不是问号 (String?) 来编写隐式展开的可选。” ...

“隐式解包的可选项是幕后的普通可选项,但也可以像非可选值一样使用,而无需在每次访问时都解包可选值。 ”

摘自:Apple Inc. “The Swift Programming Language.” iBooks

【讨论】:

【参考方案3】:
init(nibName nibName: String!,bundle nibBundle: NSBundle!)

正在调用 Objective C 框架代码。这调用了 initWithNibName:bundle。您可以选择传递此方法的参数 nil。 Objective C 对此很好。如果您在 initiWithNibName 中传递 nil,它会尝试根据相关类的名称为您解析名称。如果你为 bundle 传入 nil,它会用 [NSBundle mainBundle] 替换它。

所以,Swift 使这个参数成为一个可选参数。它必须这样做才能允许变量可选地为零。问题是为什么它不使其成为标准可选并要求您明确解包它。答案是隐式解包的可选项在将其传递给Objective C之前立即解包它。您不需要显式解包它,因为一旦您传入参数,无论是否为nil,您都完成了设置。您不希望必须显式解包然后将其传递给 Objective C!这样做没有任何意义。

【讨论】:

所以实际上有!在参数类型中允许你传递一个 nil,一个 NSBundle?还有一个 NSBundle 对吗? @NicolasManzini 嗨,是的。你可以传入零。如果这样做,它将被框架取代。在 nibName 的情况下,它使用各种截断名称的方法被关联的 viewController 的名称替换。在 NSBundle 的情况下,它在 objc 端被 [NSBundle mainBundle] 取代。如果没有与名称对应的 Class,则在前一种情况下,会抛出错误。这能回答问题吗? 您的代码不是“调用目标 C 代码”;你的代码是一个函数接口声明。【参考方案4】:

我在这个Site上找到了以下内容

在某些情况下,您可能绝对确定 Objective-C 方法或属性永远不会返回 nil 对象引用。为了使这种特殊场景中的对象更易于使用,Swift 将对象类型导入为隐式展开的可选项。隐式展开的可选类型包括可选类型的所有安全特性。此外,您可以直接访问该值,而无需检查 nil 或自行解包。当您访问这种可选类型中的值而没有先安全地解包它时,隐式解包的选项会检查该值是否丢失。如果缺少该值,则会发生运行时错误。因此,您应该始终自己检查并解包隐式解包的可选项,除非您确定该值不会丢失。

我还不确定这是否有意义。

【讨论】:

以上是关于UIViewController init 方法中的隐式解包选项的主要内容,如果未能解决你的问题,请参考以下文章

iOS,视图控制器相关(UIViewController)

iOS5 Storyboard UIViewController 的故事板调用了哪个init方法?

UIViewController init 与 initWithNibName:bundle:

为啥我不能在 Swift 中调用 UIViewController 上的默认 super.init()?

如何为我在 Swift 中以编程方式创建的 UIViewController 子类创建 init?

如何在 init 方法中从 NIB 加载 IBOutlets?