在尝试检查返回值时,我遇到了一个奇怪的情况,我想知道如何在Swift意义上“正确地”执行此操作。
我有NSStatusItem
(名为item
),我正在尝试分配NSStatusItem
和NSImage
。当我创建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
}
所以,这里有一个问题:如果一个假设是,那么如果它不是可选的,那么检查返回值究竟是什么?
答案 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分支,但在使用时?它确实!