唷,这设法将许多不同的问题打包成少量的代码:-) 这个解释很长,所以你可能会发现你知道它的第一部分,但不妨从头开始......
NSImage(byReferencingFile) 是所谓的“可失败初始化程序”——即返回 NSImage? 的初始化程序——可能包含或不包含 NSImage 对象的可选项。
原因是,从文件创建NSImage 可能会失败(假设找不到文件)。出于这个原因,它返回一个可选的,你必须检查和解包:
if let img = NSImage(byReferencingFile: somePath) {
// img will be a valid NSImage
}
else {
// img was not valid, handle error here if you want
}
现在,如果您尝试将 NSImage?(由可失败初始化程序返回)分配给非可选的 NSImage,您将收到错误消息,因为您不能这样做 - 您需要这样做如上所示的展开:
// error: value of optional type 'NSImage?' not unwrapped; did you mean to use '!' or '?'?
let img: NSImage = NSImage(byReferencingFile: somePath)
如上述错误消息中所述,处理此问题的一种方法不是使用if … let 解包图像,而是“强制解包”可选选项,如下所示:
let img: NSImage = NSImage(byReferencingFile: somePath)!
// note exclamation mark -^
这个! 的意思是:不要费心检查 nil,只要假设结果是有效的并解开它。这是有时一个好主意,但通常是一个非常糟糕的主意。如果你曾经强制解包一个 nil 值,你的程序将突然退出并出现错误。因此,除非您 100% 确定该值不可能为 nil(这并不常见),否则您应该避免这样做。
好的,那么Any! 对这一切有何影响?
带有! 的类型是“隐式展开的可选”。这些是可选的,但您不必手动强制解包。您可以像访问常规值一样访问它们——但同样,如果您访问过 nil 值,您的应用将退出。
所以你可以写如下:
let img: NSImage! = NSImage(byReferencingFile: somePath)
// or, this is basically the same thing:
let img = NSImage(byReferencingFile: somePath) as NSImage!
现在你可以使用img 好像它不是可选的。但如果它像以前一样为零,它就会崩溃。不同之处在于,当你使用 img 时它会崩溃,而不是当你将它分配给img 时。
最后,Any 是一种可以容纳任何类型的类型,但如果不取出真正的类型,它就毫无用处。您通常应该使用as? 执行此操作。 as? 检查 Any 是否包含该类型,并返回一个包含该值的可选项,如果包含其他类型,则返回 nil。
如果你将一个可选项放入Any,然后尝试将其提取为非可选项,它将失败并返回 nil,因为你输入的类型与你尝试获取的类型不同出去。
如果您使用不带问号的as,这将强制提取而不将其包装在可选中,并且您猜对了,如果它出错了,它将在运行时爆炸。事实上,这看起来很危险,以至于在新的 Swift 1.2 beta 版本中,它已被替换为as!,以匹配其强制解包的性质。在您的第一个示例中,您输入了 NSImage?,提取了一个非可选的 NSImage,并使用了 as,因此您获得了运行时断言。
无论如何,Any! 将上述所有功能组合成一个令人愉快的弹簧式死亡陷阱。它将愉快地采用可选类型,然后允许您将其内容提取为非可选类型而无需检查。这就是您的代码中发生的情况——您将一个可选项放入Any!,然后将其提取为非可选项。这几乎可以肯定是一个坏主意,因为如果找不到您的图像文件,它将崩溃,因此您最好使用此答案开头的代码从图像中提取值。