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