我知道as
运算符是做什么以及如何使用它们。但我对as
运算符的架构方面更好奇。为什么会出现在那里?最大的原因是什么?它有什么帮助?
例:
var objectData: NSData = NSUserDefaults.standardUserDefaults().objectForKey("myKey") as NSData
。如果是:
var objectData: NSData = NSUserDefaults.standardUserDefaults().objectForKey("myKey")
困惑是对的,因为在 Swift 1.2 之前,as
关键字被重载了多种含义。 Swift 1.2 使这一点更加清晰,因为现在有 3 个版本的as
:as
、as?
和 as!
。 (在以前的版本中,as
和as!
混为一谈,只是as
)
在您的示例中(我假设它低于 1.2,否则它将无法编译),您正在使用as
同时做两件事。
NSUserDefaults.objectForKey
的返回类型是一个AnyObject?
,一个可选的AnyObject
。 也就是说,它可以是任何类型的对象,也可能根本不是对象(因为可能没有为键"myKey"设置值)。
要利用此结果,您需要做两件事 – 解开可选包(即测试它是否包含值),并将AnyObject
转换为更有用的类型(在本例中为 NSData
)。
以下是执行此操作的方法:
if let objectData = NSUserDefaults.standardUserDefaults()
.objectForKey("myKey") as? NSData
// note the question mark --^
{
// use the value
}
else {
// handle the value not being present – perhaps set it to a default value
}
这是使用 as?
来测试,暂定地,objectForKey
返回的值是否为 NSData
类型。 if let
测试是否存在有效值,以及该值的类型是否正确。
请记住,为"myKey"存储的值可能与您想要的类型不兼容。 假设您想要的不是NSData
Int
. 你会做if let intData = NSUserDefaults.standardUserDefaults().objectForKey("myKey") as? Int
. 如果为"myKey"存储的值是字符串"foo",则无法将其转换为Int
如果不使用上面的as?
,而是使用 as
,则强制 Swift 解包值并在不检查的情况下转换类型。 如果一切顺利,那很好。 但是,如果没有值或数据与类型不兼容,您将获得运行时断言,并且程序将崩溃。
这非常危险,以至于在 Swift 1.2 中,这种对as
的使用被重命名为 as!
,感叹号部分表示危险的"强制"转换。 如果您尝试在 1.2 中编译示例代码,您将收到一个错误,询问您的意思是as?
还是as!
。
as
关键字保留用于始终安全的类型说明。 例如,假设您有一个重载函数,如下所示:
func f(i: Int?) { println("called with Int") }
func f(s: String) { println("called with String") }
// try to call f with nil – Swift will complain because it
// doesn't know if this is a nil int or a nil string
f(nil)
// you can tell it which with as:
f(nil as Int?) // prints "called with Int"
f(nil as String?) // prints "called with String"
还有一些类型的 Swift <-> Objective-C 桥接转换可以保证始终成功,您也可以将其as
用于:
let a = [1,2,3]
// you can always convert a Swift array to an NSArray
let n = a as NSArray
as
的好处是它确认其左侧的对象属于其右侧的类型。例如,它区分以下各项:
myAnimalReallyADog as Dog
和
myAnimalReallyACat as Dog
如果没有它,您将不得不使用其他可能更冗长的方法来测试对象转换是否合法。
as
概念在许多语言中都很常见,并不是 Swift 独有的,所以它是一个成熟的范式,而且很明显很有用。
首先,我们应该知道何时使用它。通常,使用此运算符来执行"向下转换" - 也就是说,运算符尝试将对象引用作为派生类返回 - 并且如果对象实际上是该类型的对象,则成功。这意味着,调用站点必须猜测对象的实际类型。这种倒闭也可能失败。
因此,答案实际上是双重的:
正如OldPeculier已经指出的那样,"这是一个成熟的范式,显然是有用的"。
但是,还有其他更好的设计可以避免可能失败的向下阴影。频繁使用下垂是一种代码气味。你应该努力重新思考你的设计,可能使用协议,或者类扩展等等。