gpt4 book ai didi

ios - 从其他地方合并 @Published 属性 : get current value during update,

转载 作者:行者123 更新时间:2023-11-29 11:25:13 24 4
gpt4 key购买 nike

我的主要问题是我正在尝试解决(未记录的)事实,即 @Published 属性在订阅者收到更改通知之前不会更新属性的值。我似乎找不到解决它的好方法。

考虑以下 Subject@Published 属性的人为组合。首先,一个简单的类:

class StringPager {
@Published var page = 1
@Published var string = ""
}
let pager = StringPager()

然后是一个简单的直通主题:

let stringSubject = PassthroughSubject<String, Never>()

为了调试,让我们订阅字符串属性并打印出来:

pager.$string.sink { print($0) }

到目前为止一切顺利。接下来,让我们订阅主题并根据其值更改寻呼机:

stringSubject.sink { string in
if pager.page == 1 {
pager.string = string
} else {
pager.string = string.uppercased()
}
}

希望这个逻辑能让我们在不在第一页时将寻呼机字符串设为大写。

现在让我们在页面更新时通过 stringSubject 发送值:

pager.$page.sink { 
$0 == 1 ? stringSubject.send("lowercase") : stringSubject.send("uppercase")
}

如果我们的逻辑正确,那么小写字母将始终小写,而大写字母将始终大写。不幸的是,事实并非如此。这是一个示例输出:

pager.page = 1 // lowercase
pager.page = 2 // uppercase
pager.page = 3 // UPPERCASE
pager.page = 4 // UPPERCASE
pager.page = 1 // LOWERCASE
pager.page = 1 // lowercase

原因是当我们订阅主题时,我们检查 pager.page 的值...但是更新 pager.page 是触发主题的闭包,因此 pager.page 还没有更新值,因此主体执行了错误的分支。

我已经尝试通过在下沉之前将 pager.$page 与主题一起zip 来解决这个问题:

stringSubject.zip(pager.$page).eraseToAnyPublisher().sink { ...same code... }

以及combineLatest它:

stringSubject.combineLatest(pager.$page).eraseToAnyPublisher().sink { ...same code... }

但这会导致完全相同的观察到的行为(在前一种情况下)或同样不受欢迎的行为,除了更多(在后一种情况下)。

如何获取主题 sink 闭包中的当前页面?

最佳答案

问题是您实际上并没有使用 Combine 框架的强大功能。您不应该使用 sink 闭包来查看任何值。这颠覆了Combine的全部意义!相反,您应该让该值流入您的接收器,作为您构建的 Combine 管道的一部分。

让我们从您的 StringPager 开始:

class StringPager {
@Published var page = 1
@Published var string = "this is a test"
}

让我们有一个包含 StringPager 的 View Controller 类,以及一些设置其字符串或页码的测试按钮:

class ViewController: UIViewController {
let pager = StringPager()
var storage = Set<AnyCancellable>()
override func viewDidLoad() {
// ...
}
@IBAction func doButton(_ sender: Any) {
let i = Int.random(in: 1...4)
print("setting page to \(i)")
self.pager.page = i
}
@IBAction func doButton2(_ sender: Any) {
let s = ["Manny", "Moe", "Jack"].randomElement()!
print("setting string to \(s)")
self.pager.string = s
}
}

你看这个试验台的想法是什么。我们单击第一个按钮或第二个按钮,然后将寻呼机的页面或字符串随机设置为一个新值,并在控制台中报告它是什么。

我们现在的目标是创建一个 Combine 管道,它会在每次字符串或页面更改时吐出寻呼机字符串的大写或非大写版本,具体取决于寻呼机的页面是否为 1。我将在 viewDidLoad 中执行此操作。 Combine 运算符监视任何多个发布者的变化并在其中任何一个发生变化时报告它们的当前值是 combineLatest,所以这就是我将使用的:

    pager.$page.combineLatest(pager.$string)
.map {p,s in p > 1 ? s.uppercased() : s}
.sink {print($0)}.store(in:&storage)

就是这样!现在我将点击按钮几次,让我们看看我们得到了什么:

this is a test
setting page to 3
THIS IS A TEST
setting page to 3
THIS IS A TEST
setting page to 1
this is a test
setting string to Jack
Jack
setting string to Manny
Manny
setting page to 1
Manny
setting page to 1
Manny
setting page to 4
MANNY
setting string to Manny
MANNY
setting string to Moe
MOE
setting string to Jack
JACK

如您所见,每次我们更改页面或字符串时,我们都会从管道末端获得一个新值,而且它是正确的值!如果页面大于 1,我们得到字符串的大写版本;如果页面为 1,我们将按原样获取字符串。任务完成!

关于ios - 从其他地方合并 @Published 属性 : get current value during update,,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59242482/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com