Swift5.4 中可选的误报

Posted

技术标签:

【中文标题】Swift5.4 中可选的误报【英文标题】:False positive on optional in Swift5.4 【发布时间】:2021-05-10 15:54:05 【问题描述】:

我有一个 Dictionary 扩展,它在 Xcode 12.5 和 Swift 5.4 发布之前一直运行良好。 问题是,在 Swift5.4 中似乎发生了变化,使得可选的可选返回作为误报。在某些方面它确实有意义,但现在我的扩展失败了。

考虑以下示例代码:

let dictionary: [String: Any] = ["foo": "bar"]

if let foo = dictionary["foo"] as? String? 
    debugPrint("Foo exists")


if let bar = dictionary["bar"] as? String? 
    debugPrint("bar exists")


在 Swift 5.4 之前,这将导致以下结果:

"Foo exists"

但是现在在 Swift 5.4 中我得到以下结果:

"Foo exists"
"bar exists"

这是因为即使字典被定义为[String: Any]String? 符合Any 类型,因此nil 符合any,因此 if 情况将触发,因为 nil 是 any .

对我来说,这给我正在使用的 require 函数带来了一个问题,如果泛型类型设置为可选,它就会触发。我的分机:

enum DictionaryExtractionError: Error 
    case missingProperty(String), casting(String)


extension Dictionary where Key == String, Value == Any 
    func require<T>(_ name: String) throws -> T 
        if let prop = self[name] as? T, (prop as Any?) != nil 
            return prop
        
        throw self[name] == nil ? DictionaryExtractionError.missingProperty(name) : DictionaryExtractionError.casting(name)
    

    func require<T>(_ name: String, fallback: T) -> T 
        do 
            return try require(name) ?? fallback
         catch 
            return fallback
        
    


let dictionary: [String: Any] = ["fooBar": "barFoo"]
let valid: String = dictionary.require("fooBar", fallback: "no Foobar")
let validMissing: String = dictionary.require("doesntExist", fallback: "no Foobar")
let inValidMissing: String? = dictionary.require("doesntExist", fallback: "no Foobar")

debugPrint(valid) // Should return: "barFoo"
debugPrint(validMissing) // Should return: "no Foobar"
debugPrint(inValidMissing) // Should return: Optional("no Foobar") <<<<<< This one is wrong

我的问题:有没有办法检查T 是否是可选类型?如果是这样,我可以调整我的代码来检查泛型类型在哪里是可选的,确保 prop 不为零。

【问题讨论】:

【参考方案1】:

您的示例代码也在 Xcode 12.4 中打印 "bar exists"

不管怎样,试试这个require 的实现吧:

func require<T>(_ name: String) throws -> T 
    guard let index = self.index(forKey: name) else 
        throw DictionaryExtractionError.missingProperty(name)
    
    guard let t = self[index].value as? T else 
        throw DictionaryExtractionError.casting(name)
    
    return t

【讨论】:

谢谢。如果让我忘记了索引方法,我就陷入了困境。很快就会试一试,但从外观上看,它看起来很棒。以为我把它固定在小例子上,无论哪种方式,扩展都不能在 5.4 上运行,但在 5.3 上运行(尽管我在上面的例子中对其进行了一些修改)。再次感谢! 最终将其更改为 guard let prop = self[name] else … guard let castedProp = prop as? T else … 。这是因为无论如何我都没有使用索引,它使阅读更清晰。

以上是关于Swift5.4 中可选的误报的主要内容,如果未能解决你的问题,请参考以下文章

使用 pytest 测试 __init__.py 中可选依赖项的导入:Python 3.5 /3.6 的行为不同

C#中可选参数和具名参数的使用

对象中可选字段的流类型

Swift 中可选标识符中感叹号的含义? [复制]

Swift:如果变量 a 是非可选的,那么为啥变量 b 是可选的? [复制]

Elasticsearch:文档中可选字段的意外相关性得分