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。