将 NSImage 类型转换为 Any 并将其向下转换回 NSImage

Typecast NSImage to Any and downcast it back to NSImage

我想了解为什么

var tmp: Any = NSImage(byReferencingFile: somePath)
var img: NSImage = tmp as NSImage

不会工作并导致错误,但是

var tmp: Any = NSImage(byReferencingFile: somePath) as Any!
var img: NSImage = tmp as NSImage

会做这份工作吗?有什么不同?

因为 NSImage(byReferencingFile: somePath) return 是一个 可选的 。如果您查看文档,您会发现签名是:

init?(byReferencingFile filename: String)

init 之后的问号表示初始化程序可能会失败,并且它 return 是一个 可选的 值。因此,例如当文件不存在时,它将 return nil 而不是 NSImage 实例。

您可以 fix 通过这样做...

var tmp: Any = NSImage(byReferencingFile: somePath)
var img: NSImage? = tmp as? NSImage

...虽然这并不是真正的修复。

您的第二个示例有效,因为您使用 Any! 强制解包可选。你基本上是说,忽略可选的值是什么,只给我它包装的图像。但是,如果无法加载图像,这将崩溃。

理想情况下,您想要做的是:

if let image = NSImage(byReferencingFile: somePath) {
    // Here the "if let" has checked and unwrapped the optional
    // and you can be sure that image is not nil.
}

有帮助吗?

呸,这设法将许多不同的问题打包到少量代码中:-)这个解释很长,所以你可能会发现你知道它的第一部分,但不妨从头开始......

NSImage(byReferencingFile) 是所谓的“可失败初始化器”——也就是说,return 是一个 NSImage? 的初始化器——一个可能包含也可能不包含 NSImage对象。

原因是,从文件创建 NSImage 可能会失败(假设找不到文件)。出于这个原因,它 return 是一个您必须检查和解包的可选项:

if let img = NSImage(byReferencingFile: somePath) {
    // img will be a valid NSImage
}
else {
    // img was not valid, handle error here if you want
}

现在,如果您尝试将 NSImage?(由可失败初始化程序 return 编辑)分配给非可选 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 是否包含该类型,并且 return 是一个包含该值的可选值,如果它包含其他类型则为 nil。

如果你把一个可选的放入Any,然后尝试将它提取为一个非可选的,它会失败并且return nil,因为你放入的类型不是和你想出去的类型一样。

如果你使用不带问号的 as,这将强制提取而不将其包装在可选中,而且,你猜对了,如果它出错,将在运行时爆炸。事实上,在新的 1.2 beta 版本 Swift 中,这看起来很危险,但已被替换为 as!,以匹配其强制展开的特性。在你的第一个例子中,你输入了一个 NSImage?,你正在提取一个非可选的 NSImage,并使用 as,所以你得到了一个运行时断言。

总之,Any! 将上述所有功能结合到一个令人愉快的 spring 加载的死亡陷阱中。它会愉快地采用可选类型,然后允许您将其内容提取为非可选类型而无需检查。这就是你的代码中发生的事情——你将一个可选的放入 Any!,然后将其作为非可选的提取出来。这几乎肯定是个坏主意,因为如果找不到您的图像文件,它就会崩溃,因此您最好使用此答案开头的代码从图像中提取值。