如何正确检查非可选返回值是否有效?

时间:2014-06-26 20:37:15

标签: swift

在尝试检查返回值时,我遇到了一个奇怪的情况,我想知道如何在Swift意义上“正确地”执行此操作。

我有NSStatusItem(名为item),我正在尝试分配NSStatusItemNSImage。当我创建NSImage时,因为我传递了一个图像名称的字符串值,我想确保NSImage实际上是有效的(如果我输错图像名称字符串怎么办?)。 / p>

我尝试的第一件事是:

if let image: NSImage? = NSImage(named: "CorrectIconName") {
    item.image = image
}

但这会产生错误“条件绑定中的绑定值必须是可选类型”。我以为image: NSImage?说明它是可选的,但我猜不是。

我改变了这个:

let image: NSImage? = NSImage(named: "CorrectIconName")
if image {
     item.image = image
}

哪个工作完全正常。但是我不知道为什么会这样,而第一个例子却没有。看起来或多或少完全相同。由于第一个没有编译,我以为我会尝试其他一些路线...

由于NSImage(named:)确实返回NSImage而不是NSImage?,我想我会看到如果我将构造函数的返回值直接赋给item会发生什么:

item.image = NSImage(named: "CorrectIconName")

哪个有效,但不允许我想做的错误检查。如果字符串错误,NSStatusItem会获得nil图片,这会导致我有一个不可见的状态栏项。

接下来,我尝试了这个:

let image: NSImage = NSImage(named: "CorrectIconName")
if image {
    item.image = image
}

但是这给出了错误“Type'NSImage'没有确认协议'LogicValue'”,我想这意味着你不能用if语句检查它是nil

但是,您可以通过执行以下操作来检查它是否为零:

let image: NSImage = NSImage(named: "CorrectIconName")
if image != nil {
    item.image = image
}

所以,这里有一个问题:如果一个假设是,那么如果它不是可选的,那么检查返回值究竟是什么?

4 个答案:

答案 0 :(得分:15)

实际上可选,编译器只是没有显示给你。

在Apple关于使用Objective-C对象的文档中,它说从Objective-C API导入的所有对象实际上都是隐式解包的选项(就像我们用!手动声明的那样):

  

在某些情况下,您可能完全确定Objective-C   方法或属性永远不会返回nil对象引用。要做   这个特殊场景中的对象更方便使用,Swift   将对象类型导入为隐式解包的选项。隐   未包装的可选类型包括的所有安全功能   可选类型。此外,您可以直接访问该值而无需   检查nil或自行展开。 [source]

不幸的是,编译器/语法检查器不会这样对待它们。因此,正确的检查方法是将image声明为NSImage初始化程序实际返回的类型,隐式解包的可选NSImage

let image: NSImage! = NSImage(named: "CorrectIconName")
if image {
    // do something
}

替代方法(来自@vacawama):

if let image = NSImage(named: "CorrectIconName") as NSImage! {
    // do something
}

答案 1 :(得分:1)

Objective-C初始化器之间存在一些不匹配,它们可能返回nil和swift的init语义。如果您调用由该语言定义的SomeObject(...)来创建SomeObject NOT SomeObject?

的实例

在Cocoa / Foundation框架中有一些初始化器的例子,从Swift的角度来看,生成NSSomething?而不是隐含的NSSomething

Apple有很多方法可以在不影响Swift类型系统的情况下解决这个问题,但是在他们这样做之前,有几种方法可以让你做出正确的事情。"做正确的事情。"

在条件中重新包装对象 - 这可行,但很笨重:

var maybeImage: NSImage? = NSImage(named: "CorrectIconName")
if let image = maybeImage {

}

最好是制作自己的加载NSImage的功能

func loadImageNamed(name: String) -> NSImage? {
    return NSImage(named: name)
}

func doSomethingWithAnImage() {
    if let image = loadImageNamed( "CorrectIconName") {
        ....
    }
}

或者你可以扩展NSImage。

extension NSImage {
    class func imageWithName(name: String) -> NSImage? {
        return NSImage(named: name)
    }
}

现在你可以做自然事了。

if let image = NSImage.imageWithName("CorrectIconName") {
}

对于Swift / Cocoa API来说,这似乎是正确的,我希望Apple在未来做类似的事情。

答案 2 :(得分:0)

除非我遗漏了一些完全明显的东西,否则这更多是一种观点问题。您应该将返回存储在变量中,然后检查该变量的内容。我认为这主要是因为如果返回是可选的,那么变量最终没有被声明? if语句会合适!

这与你问题的第一部分有关,这似乎也是你提出的其他问题,但我可能会感到困惑......

答案 3 :(得分:0)

我在IOS中遇到了类似的问题并使用?-operator解决了它:

if let im = UIImage(named: "Meetup.png")? {
    imageLayer.contents = im.CGImage
}

没有?它不会进入if分支,但在使用时?它确实!