TLDR:Foo!
就当它是Foo
。
许多 Cocoa 调用包含隐式展开的可选选项,他们对它的需求很可能是该功能甚至存在的原因。以下是我建议的思考方式。
首先,让我们考虑一个不涉及AnyObject
. 我认为UIDevice
是一个很好的例子。
class func currentDevice() -> UIDevice!
这里发生了什么?嗯,总有一个currentDevice
。如果返回nil
,则表明系统中存在某种深度错误。所以如果我们在 Swift 中构建这个接口,这很可能会返回UIDevice
并完成它。但是我们需要连接到 Objective-C,它返回UIDevice*
. 现在这不应该是nil
,但它在语法上可能是nil
。现在在 ObjC 中,我们通常会忽略这个事实并且不在nil
这里检查(特别是因为nil
-messaging 通常是安全的)。
那么我们如何在 Swift 中表达这种情况呢?好吧,从技术上讲,它是一个Optional<UIDevice>
,你最终会得到:
class func currentDevice() -> UIDevice?
并且您每次使用它时都需要显式地打开它(最好使用if let
块)。那会很快让你发疯,而且毫无意义。currentDevice()
总是返回一个值。这Optional
是桥接到 ObjC 的工件。
所以他们发明了一个hack来解决这个问题(我认为这确实是一个hack;如果没有ObjC,我无法想象构建这个功能)。那个hack说,是的,它是一个Optional
,但你可以假装它不是,我们保证它总是一个值。
那就是!
。对于这种东西,您基本上忽略了!
并假装它正在将您退回UIDevice
并继续前进。如果他们对你撒谎并返回nil
,那么,那将会崩溃。他们不应该对你撒谎。
这暗示了一个规则:除非你真的需要,否则不要使用!
(而且你几乎只需要在桥接到 ObjC 时使用)。
在您的具体示例中,这在两个方向上都有效:
func valueForAttribute(key: String!) -> AnyObject!
从技术上讲,它需要一个Optional<String>
,但只是因为它桥接到NSString*
. 你必须通过非nil
这里。从技术上讲,它会返回您Optional<AnyObject>
,但这只是因为它已桥接到id
. 它保证不会nil
。