使用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)
时,即使 latest
是 String?
,它会自动重新路由 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/