【问题标题】:Typecast NSImage to Any and downcast it back to NSImage将 NSImage 类型转换为 Any 并将其向下转换回 NSImage
【发布时间】:2015-03-07 19:20:16
【问题描述】:

我试图理解为什么

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

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

【问题讨论】:

    标签: swift casting nsimage any


    【解决方案1】:

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

    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!,然后将其提取为非可选项。这几乎可以肯定是一个坏主意,因为如果找不到您的图像文件,它将崩溃,因此您最好使用此答案开头的代码从图像中提取值。

    【讨论】:

    • 感谢您的出色回答。我现在看到(如果我是正确的)在第一个示例中我试图将 Optional 转换为 NSImage。回到一个例子:有没有一种简单的方法可以从 Any 取回 NSImage 而不将其声明为可选 (var tmp: Any? = NSImage(byReferencingFile: somePath)),因为这可能是我真正的问题?
    【解决方案2】:

    因为NSImage(byReferencingFile: somePath) 返回一个可选。如果您查看文档,您可以看到签名是:

    init?(byReferencingFile filename: String)

    init 后面的问号表示初始化程序可能失败,并且它返回一个 可选 值。因此,例如当文件不存在时,它将返回 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.
    }
    

    这有帮助吗?

    【讨论】:

    • 感谢分析器,是的,它有帮助!由于 Any,我完全忽略了 Any 不是 NSImage 而是 Optional 的事实。建议的“修复”(是的,我知道我不应该这样做,但这只是一个理论上的问题)var img: NSImage? = tmp as? NSImage 我认为它总是会为 img 变量分配一个 nil 值,即使tmp 变量包含一个有效的 NSImage
    猜你喜欢
    • 2012-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多