为什么没有Swift nil-coalescing三元运算符返回unwrapped类型?

时间:2015-01-31 21:25:32

标签: swift conditional-operator

我读到三元运算符??打开一个可选项,如果它不是nil,但如果我这样做:

var type: String?
type = "milk"
let certainType = type ?? "melon"

然后,CertainType仍然是String?,如果我这样做

println("it's a \(certainType)")

它会打印出来:

it's a Optional("milk")

思想?

更新:

很抱歉这个混乱 - 我的意思是var类型:字符串?

我知道它应该打印"它是牛奶",但我在控制台中看到的是"它是可选的("牛奶" )" - 其他人遇到过同样的问题?这可能是由字符串插值引起的吗?

@Antonio提问,这里有更多的上下文和实际代码和日志记录快照 - 类型来自Note,它是一个用于处理xcdatamodel的NSManagedObject类

class Note: NSManagedObject {
  @NSManaged var type: String?
}

我的类型设置为' todo'在某些时候,然后有以下代码将它们打印出来:

println("type class:\(_stdlib_getDemangledTypeName(note.type))")
let type1 = note.type ?? "note"
println("type1:\(type1)")
let type2: String = note.type ?? "note"
println("type2:\(type2)")

输出:

type class:Swift.Optional
type1:Optional("todo")
type2:todo

正如您所看到的,如果我没有明确地将type1的类型标记为String,它将打印不需要的结果可选(" todo") - 我使用的类型用于构造路径的字符串插值使其重要

8 个答案:

答案 0 :(得分:6)

我无法证明我要说的是什么,所以非常感谢任何反馈

OP断言代码类似于:

var type: String? = "milk"
let certainType = type ?? "melon"
println("it's a \(certainType)")

打印一个意外的字符串:

  

“这是一个可选的(”牛奶“)”

应该是:

  

“这是牛奶”

事实证明,当变量实际上是具有@NSManaged属性的属性时会发生。

我怀疑类型推断存在错误。 OP表示:

let certainType = type ?? "melon"

打印错误的结果,而:

let certainType: String = type ?? "melon"

打印正确的。

所以由于某种原因,在没有明确指出变量类型的情况下,nil coalescing运算符返回一个可选的

如果我将type变量的类型更改为AnyObjectAnyObject?,则会实际打印意外结果

var type: AnyObject = "milk"
let certainType = type ?? "melon"
println("it's a \(certainType)")
  

“这是一个可选(牛奶)”

我的猜测是:因为使用了@NSManaged属性,所以在使用时会使用错误的类型(AnyObject?)推断属性,除非明确指出了正确的类型

至于为什么会这样,不知道(除了认为这是一个错误)

随意向上或向下投票这个答案,最重要的是不要将其视为一种解决方案 - 我会很感激反馈,因为我很想知道发生了什么,以及它是否真的是一个错误。

答案 1 :(得分:3)

这是(我认为)设计。如果你这样做:

let type = "mil"
println("it's a \(type)")

如果我没有弄错的话,这将打印it's a "milk",这在调试时比it's a String更有用。请注意,\(...)与Objective-C中的%@类似:类可以覆盖它们的字符串表示。

另外,正如安东尼奥已经指出的那样:

  1. 您无法更改不可变变量(即let)。
  2. 因此,变量let变量无效(即不要let type: String? = "milk",因为它显然永远不会是零)。

答案 2 :(得分:3)

  

我读到三元运算符??如果它不是零,则展开一个可选项,但是......

它打开它。

  

让类型:字符串?   type =“milk”   让certainType = type ?? “甜瓜”

您的代码将无法编译,但如果您将let更改为var,则它将起作用,而且CertainType将为String类型。

var type: String?
type = "milk"
let certainType = type ?? "melon"

println("it's a \(certainType)") // prints "it's a milk"

答案 3 :(得分:2)

这称为Nil Coalescing Operator

如果您有可选aa ?? b的结果将是a! a不是nilb的结果是a != nil ? a! : b 是。它是这个表达式的简写:

let type: String? = "milk"

因此,您正确使用运算符,但必须在声明它的同一行初始化常量:

var

但是,我假设你不希望在这种情况下成为常数(因为你要检查它是什么),所以如果你使用{{1}}来声明它,你现有的代码应该可以工作。

答案 4 :(得分:0)

这就是你想要的

let certainType = type! ?? "melon"

注意我打开了类型。它适用于你:

let type2: String = note.type ?? "note"

因为你基本上是在你的演员表中展开给String。

答案 5 :(得分:0)

我遇到了类似的问题。

我有一个[String : AnyObject]类型的字典,但我之后的条目结果是String值。担心它可能并不总是存在。我使用以下代码检索它:

let message = dictionary["message"] ?? "No message"

但是,当条目存在时,仍然会给出字符串:

  

可选(实际消息)

即,变量message的类型为String?

我把它改为:

let message = (dictionary["message"] as? String) ?? "No message"

...现在检索到的值是一个未包装的非可选字符串。

答案 6 :(得分:0)

我的案例中的问题是类型@NSManaged的{​​{1}}属性,我试图用字符串包装。

NSNumber?

此问题的解决方案是将 let time = "\(nsManagedObject.optionalNsNumber ?? "") months" /// result "Optional(<random number>) months" 转换为NSNumber。下面是按预期工作的代码。

stringValue

如果值包含在字符串中,Swift编译器不会发现问题。如果您尝试使用 let time = "\(nsManagedObject.optionalNsNumber.stringValue ?? "") months" /// result "<random number> months" 运算符,请将结果添加到字符串中,它将指向手头的错误nil coalescing

Can't add NSObject and String属性包装到字符串中时要小心。

答案 7 :(得分:0)

我发现有一种尝试方法。

infix operator ???


/// use this custom operator to overcome issue related to '??' which always needs assignment to remove Optional
///
/// - parameter lhs: left hand side variable
/// - parameter rhs: right hand side variable
///
/// - returns: it will return unwrapped value of one of the variable which has not nil value
func ???(lhs: Any?, rhs: Any?) -> Any {

    let temp : Any = lhs ?? rhs

    return temp

}