ios - Swift Combine compactMap 不起作用(Xcode 13)

标签 ios swift xcode combine

使用Combine 的assign(to:) 得到了意想不到的结果直接在 compactMap 之后.这是代码,它在 Xcodes Playground, Xcode Version 13.0 (13A233) 中对我来说是 100% 可重现的。

import Combine
import Foundation

class Test {
    @Published var current: String?
    @Published var latest1: String?
    @Published var latest2: String?
    @Published var latest3: String?
    
    init() {
        // Broken? - compactMap passes nil downstream
        self.$current
           .compactMap { $0 }
           .assign(to: &self.$latest1)
        
        // Fixed by specifying the transform closure type explicitely
        self.$current
            .compactMap { value -> String? in value }
            .assign(to: &self.$latest2)
        
         // Fixed by an additional map inbetween
         self.$current
            .compactMap { $0 }
            .map { $0 }
            .assign(to: &self.$latest3)
    }
}

let obj = Test()

obj.current = "success"

print("current: \(String(describing: obj.current))")
print("latest1: \(String(describing: obj.latest1))")
print("latest2: \(String(describing: obj.latest2))")
print("latest3: \(String(describing: obj.latest3))")
print("")

obj.current = nil

print("current: \(String(describing: obj.current))")
print("latest1: \(String(describing: obj.latest1))") // nil shouldn't arrive here
print("latest2: \(String(describing: obj.latest2))")
print("latest3: \(String(describing: obj.latest3))")

// Output:
//current: Optional("success")
//latest1: Optional("success")
//latest2: Optional("success")
//latest3: Optional("success")
//
//current: nil
//latest1: nil
//latest2: Optional("success")
//latest3: Optional("success")
也许我在这里错过了一些明显的东西?或者这可能是Combine中的一个错误?。感谢您的关注。

更新:我用更简洁的版本更新了示例代码

最佳答案

这里的问题是 Swift 的类型推断机制,Combine 中没有错误。让我解释一下原因。compactMap(transform:)映射一个 Value?T ,但是在您的情况下 T ( self.latest 的类型)实际上是 String? ,又名字符串可选。所以整个管道被重新路由到 String?输出,与您在屏幕上看到的相匹配。
当你不指定涉及的类型时,Swift 急于满足你的代码需求,所以当它看到 assign(to: &self.$latest) 时,即使 latestString? ,它会自动重新路由 compactMap来自 (String?) -> String(String?) -> String? .而且几乎不可能得到 nil这种转换的值(value):)
基本上,“有问题”的管道推断出以下类型:

self.$current
    .compactMap { (val: String?) -> String? in return val }
    .assign(to: &self.$latest) 
,这是因为它在 Swift 中完全有效到 compactMap从一个可选到另一个可选。
试试写 @Published var latest: String = "" ,您将获得预期的行为。请注意,我说的是“预期”,而不是“正确”,因为“正确”取决于您的代码的外观。
另外,尝试将 compactMap 一分为二管道:
let pub = self.$current.compactMap { $0 }
pub.assign(to: &self.$latest) // error: Inout argument could be set to a value with a type other than 'Published<String?>.Publisher'; use a value declared as type 'Published<String>.Publisher' instead
基本上,通过分配一个可选属性,您可以决定整个管道的行为,如果一切顺利,并且没有类型不匹配,您最终可能会出现意外行为(不是错误行为,只是意外行为)。

关于ios - Swift Combine compactMap 不起作用(Xcode 13),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69403867/

相关文章:

objective-c - 从公共(public) header 引用私有(private) header

ios - 导航栏样式

ios - 在 Swift 中获取 RandomPlusMinus

ios - 以编程方式在 iPhone 应用程序上显示键盘

ios - 去 Segue 还是去 didSelectRowAtIndexPath?

objective-c - 如何在 Swift 中返回实例类型

ios - 核心数据更改是否反射(reflect)在本地 NSManagedObject 子类变量中?

ios - 如何获取 Facebook 用户资料(swift)

ios - 使用 sort 函数按 NSDates 对数组进行排序

Xcode 9 启用服务器失败,因为 "xcsd"钥匙串(keychain)密码