在 SwiftUI 中,
.task
修改器将在视图被移除时自动取消。因此,由于结构化并发,通知的 AsyncSequence 也将被取消:
struct DetailsView: View {
var body: some View {
Button("Dismiss") {
...
}
.task {
await addNotificationsHandler()
}
}
func addNotificationsHandler() async {
for await notification in NotificationCenter.default.notifications(named: .foo) {
foo(with: notification)
}
}
}
在UIKit中,你必须更加小心,因为异步序列不会自动取消。因此,你需要跟踪该
AsyncSequence
的
Task
并在视图消失时
cancel
它:
private var notificationsTask: Task<(), Never>?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
notificationsTask = Task {
await addNotificationsHandler()
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
notificationsTask?.cancel()
}
func addNotificationsHandler() async {
for await notification in NotificationCenter.default.notifications(named: .foo) {
foo(with: notification)
}
}
底线是,在上述的UIKit示例中,你必须手动取消循环遍历
AsyncSequence
的
Task
。在SwiftUI中,
.task
修饰符会自动为你取消,因此
AsyncSequence
将被自动取消。
底线是,如果你在
Task
内开始循环遍历
AsyncSequence
,那么请确保在完成后
cancel
该
Task
。根据定义,
Task
不遵循结构化并发性,因此你必须自己处理取消操作。
如果您有多个通知需要观察,可以使用任务组。例如,在SwiftUI中,以下是一个示例,观察应用程序何时变为非活动状态以及何时变为活动状态:
struct SampleView: View {
@StateObject var viewModel = SampleViewModel()
var body: some View {
VStack { … }
.task {
await viewModel.addNotificationsHandlers()
}
}
}
@MainActor
class SampleViewModel: ObservableObject {
func addNotificationsHandlers() async {
await withTaskGroup(of: Void.self) { group in
group.addTask { await self.addBecomeActiveHandler() }
group.addTask { await self.addResignActiveHandler() }
}
}
func addBecomeActiveHandler() async {
for await notification in NotificationCenter.default.notifications(named: UIApplication.didBecomeActiveNotification) {
handleBecomeActive(for: notification)
}
}
func addResignActiveHandler() async {
for await notification in NotificationCenter.default.notifications(named: UIApplication.willResignActiveNotification) {
handleResignActive(for: notification)
}
}
func handleBecomeActive(for notification: Notification) { … }
func handleResignActive(for notification: Notification) { … }
}
或者在UIKit中:
private var notificationsTask: Task<(), Never>?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
addNotificationsHandlers()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
removeNotificationsHandlers()
}
func addNotificationsHandlers() {
notificationsTask = Task {
await withTaskGroup(of: Void.self) { group in
group.addTask { await self.addBecomeActiveHandler() }
group.addTask { await self.addResignActiveHandler() }
}
}
}
func removeNotificationsHandlers() {
notificationsTask?.cancel()
}
func addBecomeActiveHandler() async {
for await notification in NotificationCenter.default.notifications(named: UIApplication.didBecomeActiveNotification) {
handleBecomeActive(for: notification)
}
}
func addResignActiveHandler() async {
for await notification in NotificationCenter.default.notifications(named: UIApplication.willResignActiveNotification) {
handleResignActive(for: notification)
}
}
func handleBecomeActive(for notification: Notification) { … }
func handleResignActive(for notification: Notification) { … }
notifications
没有任何问题。(他们把它放在那里是有原因的。LOL。) 但Task
的问题在于,您选择了退出结构化并发,因此必须自行手动执行cancel
。 - Rob