可选类型 Any? 等于 nil。

3

我有:

let value: Any? = myObject[keyPath: myParamKeyPath]

myParamKeyPath是一个String?类型。

myParam应该为nil时,我有:

value == nil返回false

(value as? String?) == nil返回true

是否可能在不必先将其转换为String?的情况下检查value是否等于nil?比如将其与NSNull进行比较之类的操作吗?

另外,我无法直接将value类型更改为String?,因为它还用于我的代码中的其他类型。

编辑:

(value as? String?) == nil返回true实际上是不相关的。但我仍然可以打印由keypath指向的值,它实际上将是nil。所以我仍然不明白为什么value == nil返回false,而我期望它是true...

带有更多代码的EDIT2:

    let setting = appSettings.settings[indexPath.row]
    let value = appSettings[keyPath: setting.keyPath]

    let fontAwesome: FontAwesome

    switch setting.keyPath {
    case \PrinterSettings.printableImageIDs:
        fontAwesome = blablabla
    case \WeatherSettings.lockscreenImageIDs:
        fontAwesome = blablabla
    default:
        if let value = value as? FakeButtonPlacementSubSettings {
            fontAwesome = blablabla
        } else {
            fontAwesome = value != nil ? .checkSquareO : .squareO
        }
    }

我期望得到的是fontAwesomeIcon = .squareO,但当myObject[keyPath: myParamKeyPath]的值为String?时,我得到了checkSquareO(它在稍后的另一个值为Bool?的情况下也是如此)。我一定是在某个地方错过了什么...
编辑3截图: Screenshot 编辑4更多澄清我的目标: 首先,如果你能到达这里,再次感谢你的帮助。
以下是有关我的项目当前设计的两张照片: User model Setting usecase 我在我的项目中使用KVO。以前我使用的是objective-c字符串#keyPath。它很好用,但几乎所有的模型都必须转换成@objc。因此,我当前的目标是删除它并切换到新的Swift 4 keypath系统。
简而言之:我有一个包含许多设置(不止截图中的设置)的用户类,这些设置可以是多种类型(还有一些自定义类型)。
另一方面,我创建了大约10个“设置编辑器视图控制器”:每种设置都有一个。我想使用相同的设置编辑器VC来编辑同一类型的每个设置。
例如,如果要编辑的设置是布尔值,则希望使用“布尔编辑器VC”,并通过用户的新选择来编辑所选设置。这就是我使用KVO系统的原因。如果你们有更好的解决方案,我不想继续使用它。这也会消除我的Any? == nil问题。

5
如果value不能被强制类型转换为字符串(即无法将其转换为字符串),那么(value as? String) == nil会返回true,因此它可以是nil,也可以是整数,这都是正确的。 如果value == nil确实为真,则应该为真,否则value中就包含了某些内容。我建议您设置一个断点,在调试器中检查它实际包含的内容。 - Upholder Of Truth
@LeoDabus 我不是,那只是个例子。实际上我的代码是let value = myObject[keyPath: myParamKeyPath]。如果之后我加了 if let value = value,它实际上会进入这个if语句,然后我的value就变成了一个Any,这真的很奇怪,因为它背后其实是一个String? - Tulleb
2
你的调试屏幕显示结果为 Optional(nil),这意味着你肯定在查看一个 String?? 值。 - Rob
1
我无法强调类型 Any? 有多么混乱,如果它出现了,你必须尽快避免使用并转换为更具体的类型。Optional 本身就是类型 Any,而且任何类型都可以隐式地提升为其自身的 Optional。这意味着 AnyAny?Any??Any??? 等都是可互换的,并且可以轻松地进行提升。一旦涉及到 Any?,整个类型系统就会失效。appSettings 是什么类型?有没有任何方法可以避免它的值成为 Any - Rob Napier
1
你想用 appSettings 解决什么问题?我怀疑有更符合 Swift 风格的解决方法。你需要在那个 switch 中特别处理一些事情,这强烈暗示了这里存在设计问题。看着代码,我怀疑你真正想要的是一个带有关联值的枚举,而不是 as? 强制转换。 - Rob Napier
显示剩余18条评论
2个回答

0

以下方案如何?

struct S {
    let x: String?
}

let v1: Any? = S(x: "abc")[keyPath: \S.x]
print(v1 is String? && v1 == nil) // false

let v2: Any? = S(x: nil)[keyPath: \S.x]
print(v2 is String? && v2 == nil) // true

这正是我正在寻找的,但在我的情况下,当v2 == nil应该为true时,它却为false。也许是因为我的AnyKeyPath不够明确。 - Tulleb

0
问题在于你的代码假定该值为String?,而实际上它是String??。我认为这源于使用了AnyKeyPath
struct Foo {
    let bar: String?
}

let foo = Foo(bar: "baz")

let keyPath: AnyKeyPath = \Foo.bar
print(foo[keyPath: keyPath])                        // Optional(Optional("baz"))

let keyPath2 = \Foo.bar
print(foo[keyPath: keyPath2])                       // Optional("baz")

我想知道是否有一种方法可以调整您的代码以使用KeyPath<T,String?>模式,这样,正如您所说,您的myParamKeyPath确实明确地引用了一个String?,例如:

let foo = Foo(bar: "baz")

let keyPath = \Foo.bar

func keyPathExperiment(object: Any, keyPath: AnyKeyPath) {
    print(object[keyPath: keyPath])
}

func keyPathExperiment2<T>(object: T, keyPath: KeyPath<T, String?>) {
    print(object[keyPath: keyPath])
}

keyPathExperiment(object: foo, keyPath: keyPath)    // Sigh: Optional(Optional("baz"))
keyPathExperiment2(object: foo, keyPath: keyPath)   // Good: Optional("baz")

您提供的代码片段不足以让我们了解如何精确地适应您的情况。

正如Rob Napier所说,我想知道是否可能有更Swifty的方法来解决这个问题。例如,当我看到像这样的switch语句时,我可能会倾向于使用枚举模式。


感谢@Rob给出的建议。我已经在原始帖子中完成了对我所尝试做的事情的解释。 - Tulleb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接