【问题标题】:What does the exclamation mark mean for a swift initializer?感叹号对于快速初始化程序意味着什么?
【发布时间】:2014-12-13 22:51:00
【问题描述】:

我见过这样的代码,XCode 从objective-c 初始化器创建:

init!(logMsg: String!, level logLevel: DDLogLevel, flag logFlag: DDLogFlag, context logContext: Int32, file: UnsafePointer<Int8>, function: UnsafePointer<Int8>, line: Int32, tag: AnyObject!, options optionsMask: DDLogMessageOptions)
init!(logMsg: String!, level logLevel: DDLogLevel, flag logFlag: DDLogFlag, context logContext: Int32, file: UnsafePointer<Int8>, function: UnsafePointer<Int8>, line: Int32, tag: AnyObject!, options optionsMask: DDLogMessageOptions, timestamp aTimestamp: NSDate!)

原代码为:

- (instancetype)initWithLogMsg:(NSString *)logMsg
                         level:(DDLogLevel)logLevel
                          flag:(DDLogFlag)logFlag
                       context:(int)logContext
                          file:(const char *)file
                      function:(const char *)function
                          line:(int)line
                           tag:(id)tag
                       options:(DDLogMessageOptions)optionsMask;
- (instancetype)initWithLogMsg:(NSString *)logMsg
                         level:(DDLogLevel)logLevel
                          flag:(DDLogFlag)logFlag
                       context:(int)logContext
                          file:(const char *)file
                      function:(const char *)function
                          line:(int)line
                           tag:(id)tag
                       options:(DDLogMessageOptions)optionsMask
                     timestamp:(NSDate *)aTimestamp;

init 关键字后面的感叹号是什么意思?

【问题讨论】:

标签: objective-c swift


【解决方案1】:

当前接受的答案给出的是什么,而不是为什么。我认为在这种情况下,了解原因尤为重要。

要直接回答您的问题,它是一个返回 implicitly-unwrapped optional 的初始化程序。

使用init? 表示初始化可能失败是处理错误的有效方法。它返回一个“可选”(例如Type?),暗示一个值已经被初始化,或者没有任何东西可以被初始化并且它的内容是nil。但是init!,它返回一个 implicitly-unwrapped 可选,什么时候有用?

隐式解包选项表明您可以确信您当前正在使用的值不是 nil 而无需检查它,但它可能在其生命周期的某个时刻为 nil。这与永远不能为零的非可选类型形成鲜明对比。由于从初始化程序获取值时,您从其生命周期的一开始就使用它,因此init! 的用例并不多。

它的存在可能主要是为了帮助 Objective-C 框架转换以避免手动检查每个自动转换的初始值设定项。 “这件事可能是 nil 但可能不是”是 Objective-C 默认的工作方式。在您的情况下,Xcode 无法知道这些方法是否 100% 的时间返回初始化值。遍历每一个框架并确定初始化是否应该返回TypeType? 是一项相当大的努力,因此Type! 同时是一个明智的默认值。作为证明,Xcode 足够聪明,可以将包含 (NSError **) 的初始化程序转换为 init?

另一个用例是委托一个可失败的初始化程序,您知道它永远不会导致失败情况。但除此之外,应该尽可能避免在自己的 Swift 代码中编写 init!(即使这种情况仍然很不确定)。

来源:

【讨论】:

  • 谢谢安德鲁,但问题是关于什么而不是为什么。一旦我知道它是什么,我就可以很容易地找到它的文档。
  • 我明白:)。我只是希望向可能遇到此问题的人提供一些无法在文档中找到的见解。我觉得在这种情况下很重要。
  • 您说过,“由于从初始化程序获取值时,您从其生命周期的一开始就使用它,因此 init! 的用例并不多。”我不同意这一点。很多时候,程序员知道 init 会成功(例如UIImage(named:)),但编译器却没有。在这些情况下使用! 是完全合适的。
  • 我明白你的意思,强制解开 init? 初始化程序绝对合适,该初始化程序第一次和此后每次都非零。我正在专门讨论隐式展开的可选可失败初始化程序—init!UIImage(named:)init? 初始化器,原因正是您提到的,编译器不知道它会成功。有趣的区别!
  • @Andrew 的答案是完整且易于理解的解决方案。干得好
【解决方案2】:

它是可失败的初始化器,在 Swift 1.1 中引入(使用 Xcode 6.1)

来自Apple Developer

初始化!可失败的初始化器

您通常会定义一个可失败的初始化程序,该初始化程序会创建一个可选的 适当类型的实例,方法是在 init 关键字 (init?)。或者,您可以定义一个失败的 创建一个隐式展开的可选实例的初始化程序 合适的类型。通过在后面加上感叹号来做到这一点 init 关键字 (init!) 而不是问号。

您可以从init? 委托给init!,反之亦然,您可以 用init! 覆盖init?,反之亦然。您也可以委托 initinit!,尽管这样做会触发断言,如果 init! 初始化程序导致初始化失败。

(强调我的)

【讨论】:

  • “从 init 委托?到 init!”是什么意思?从 init?? 调用 init!
  • 也许补充一点,Xcode 6.1 引入了可失败的初始化程序(据我所知)。
  • @MartinR 是的,使用 swift 1.1
  • “初始化器委托”只是从另一个初始化器调用特定初始化器的术语。它与委托协议无关。
【解决方案3】:

这些被称为“隐式解包选项”[1]。 logMsg 的 obj-c 类型(例如)是 NSString*,可以是 nil。它可以用作可选的 - String?,在这种情况下,您将显式打开它以获取值。细绳!将直接为您提供值,因此假定 logMsg 不会为 nil。

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

当一个可选项的值在第一次定义后立即确认存在并且可以肯定地假定在此后的每个点都存在时,隐式展开的可选项很有用。

--
1.https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html

【讨论】:

  • 哦,我误解了你的问题,错过了'init!'部分:/nicael 的回答解释了这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-12
  • 2011-01-11
  • 2015-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-18
相关资源
最近更新 更多