我的主要问题是,我试图解决(无证)的事实@Published属性在通知订阅方更改后才会更新该属性的值。我似乎找不到一个很好的方法来绕过它。
考虑以下人为组合Subject和@Published财产。首先,一个简单的类:
class StringPager {
@Published var page = 1
@Published var string = ""
}
let pager = StringPager()
然后是一个简单的通过主题:
let stringSubject = PassthroughSubject<String, Never>()
为了进行调试,让我们订阅String属性并打印出来:
pager.$string.sink { print($0) } 到目前一切尚好。接下来,让我们订阅主题并根据其值更改寻呼机:
stringSubject.sink { string in
if pager.page == 1 {
pager.string = string
} else {
pager.string = string.uppercased()
}
}
希望,这个逻辑将允许我们使寻呼机字符串当我们不在第一页时就会出现。
现在,当页面更新时,让我们通过string Subject发送值:
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还没有更新的值,因此主题执行错误的分支。
我两个人都试过了zip使.pager.$page在下沉之前:
stringSubject.zip(pager.$page).eraseToAnyPublisher().sink { ...same code... }
以及combineLatest指的是:
stringSubject.combineLatest(pager.$page).eraseToAnyPublisher().sink { ...same code... }
但这会导致完全相同的观察行为(在前一种情况下)或同样不理想的行为,除了更多的行为(在后一种情况下)。
我怎么才能拿到电流页内主题sink结束?
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
你的问题涉及到Swift的Combine框架中如何正确处理多个异步更新和依赖属性的问题。你想要在stringSubject
发送值时,根据pager.page
的当前状态来决定如何更新pager.string
。但是,由于异步和闭包捕获变量的机制,导致了你观察到的非预期行为。
解决这个问题的一个方法是确保你在处理主题发送的值时,能够访问到pager.page
的最新状态。你可以通过使用.receive(on: RunLoop.main)
来确保sink操作在主线程执行,并且利用.share()
来确保所有订阅者看到的是同一个最新的状态。同时,可以考虑使用.debounce(for:)
来避免因快速连续更改而产生的不必要的多次处理。
下面是一个修改后的示例代码,尝试解决你的问题:
import Combine
class StringPager {
@Published var page = 1
@Published var string = ""
}
let pager = StringPager()
let stringSubject = PassthroughSubject<String, Never>()
// 使用share保证所有订阅者共享同一状态
let sharedStringSubject = stringSubject.share()
// 订阅pager.$page的变化,但这里我们主要关注如何处理stringSubject
pager.$page
.receive(on: RunLoop.main) // 确保在主线程处理
.share() // 确保共享最新的page状态
.sink { [weak pager] currentPage in
guard let strongPager = pager else { return }
sharedStringSubject
.receive(on: RunLoop.main)
.debounce(for: .milliseconds(100)) // 防止快速连续触发
.sink { string in
if currentPage == 1 {
strongPager.string = string
} else {
strongPager.string = string.uppercased()
}
}
.store(in: &cancellables)
}
.store(in: &cancellables)
// 发送值到stringSubject
pager.$page
.receive(on: RunLoop.main)
.sink { $0 == 1 ? stringSubject.send("lowercase") : stringSubject.send("uppercase") }
.store(in: &cancellables)
// 订阅string以验证输出
pager.$string
.sink { print($0) }
.store(in: &cancellables)
// 示例更新
pager.page = 1 // 应输出 lowercase
pager.page = 2 // 应输出 UPPERCASE
这个解决方案的关键在于: - 使用.share()
确保所有对stringSubject
的订阅都能看到相同的状态。 - 在处理stringSubject
的sink之前,通过.receive(on: RunLoop.main)
确保在主线程上执行并等待可能的UI更新完成。 - 添加.debounce(for:)
来防止短时间内多次触发导致的不必要处理。 - 注意捕获弱引用[weak pager]
以避免循环引用。
请尝试上述代码看是否解决了你的问题。如果还有其他疑问或需要进一步的帮助,请随时告诉我。