Combine:如何在取消 AnyCancellable 时清理资源?
Combine: How to clean up resources while an AnyCancellable is being cancelled?
概览:
- 我有一个异步任务要从数据库中获取
- 我已经为异步任务创建了一个 Future(从数据库中获取)。
问题:
- 当 Future 被取消时如何执行自定义代码?
目的:
- 我希望在取消订阅时关闭数据库连接。
比如我想用Combine
重写这个辅助方法:
// Similar to https://developer.apple.com/documentation/coredata/nspersistentcontainer/1640564-performbackgroundtask
func withDatabaseFTSContext(block: @escaping (FMDatabase?) -> Void) {
queue.async {
guard let database = self.database else {
block(nil)
return
}
database.open()
let simpleTokenizer = FMSimpleTokenizer(locale: nil)
FMDatabase.registerTokenizer(simpleTokenizer, withKey: "simple")
database.installTokenizerModule()
block(database)
database.close()
}
}
我可以利用 Combine
将此方法重写为 return FMDatabase
作为发布者的参数吗?
我试图使用 Combine
但它不起作用。数据库将在cancel()
之前关闭
private func withDatabaseFTSContext() -> AnyPublisher<FMDatabase?, Never> {
return Future<FMDatabase?, Never> { promise in
self.queue.async {
guard let database = self.database else {
promise(.success(nil))
return
}
database.open()
let simpleTokenizer = FMSimpleTokenizer(locale: nil)
FMDatabase.registerTokenizer(simpleTokenizer, withKey: "simple")
database.installTokenizerModule()
promise(.success(database))
database.close() // When to close this database? Currently it will be closed before `cancel()`
}
}.eraseToAnyPublisher()
}
简短回答:没有回调触发到基础 Future,您可以使用它来清理订阅者 cancel
上的事情。在 Combine 设计中,这些功能是有意分开的,并且没有指向其发布者的参考链接。
(此外,Future 在 Combine 世界中是一个棘手的人物,因为闭包会在创建时立即调用,而不是在您有订阅时调用(如果需要,请将 Future 发布者包装在 Deferred 发布者中)).
综上所述,您可能想要解决您的潜在问题的方法是重新构建您处理此问题的方式,以分离管理 FMDB 实例和发布数据的问题。在此上下文中相当有用的一种模式是创建一个对象来保存 FMDB 引用的生命周期,并处理清理其 deinit()
上的资源。然后,您还可以拥有一个功能,该功能可以从同一对象向 Publisher 提供您需要的任何内容,然后请求的取消在语义上更改为仅取消获取数据库,而不是取消和清理数据库连接。
概览:
- 我有一个异步任务要从数据库中获取
- 我已经为异步任务创建了一个 Future(从数据库中获取)。
问题:
- 当 Future 被取消时如何执行自定义代码?
目的:
- 我希望在取消订阅时关闭数据库连接。
比如我想用Combine
重写这个辅助方法:
// Similar to https://developer.apple.com/documentation/coredata/nspersistentcontainer/1640564-performbackgroundtask
func withDatabaseFTSContext(block: @escaping (FMDatabase?) -> Void) {
queue.async {
guard let database = self.database else {
block(nil)
return
}
database.open()
let simpleTokenizer = FMSimpleTokenizer(locale: nil)
FMDatabase.registerTokenizer(simpleTokenizer, withKey: "simple")
database.installTokenizerModule()
block(database)
database.close()
}
}
我可以利用 Combine
将此方法重写为 return FMDatabase
作为发布者的参数吗?
我试图使用 Combine
但它不起作用。数据库将在cancel()
private func withDatabaseFTSContext() -> AnyPublisher<FMDatabase?, Never> {
return Future<FMDatabase?, Never> { promise in
self.queue.async {
guard let database = self.database else {
promise(.success(nil))
return
}
database.open()
let simpleTokenizer = FMSimpleTokenizer(locale: nil)
FMDatabase.registerTokenizer(simpleTokenizer, withKey: "simple")
database.installTokenizerModule()
promise(.success(database))
database.close() // When to close this database? Currently it will be closed before `cancel()`
}
}.eraseToAnyPublisher()
}
简短回答:没有回调触发到基础 Future,您可以使用它来清理订阅者 cancel
上的事情。在 Combine 设计中,这些功能是有意分开的,并且没有指向其发布者的参考链接。
(此外,Future 在 Combine 世界中是一个棘手的人物,因为闭包会在创建时立即调用,而不是在您有订阅时调用(如果需要,请将 Future 发布者包装在 Deferred 发布者中)).
综上所述,您可能想要解决您的潜在问题的方法是重新构建您处理此问题的方式,以分离管理 FMDB 实例和发布数据的问题。在此上下文中相当有用的一种模式是创建一个对象来保存 FMDB 引用的生命周期,并处理清理其 deinit()
上的资源。然后,您还可以拥有一个功能,该功能可以从同一对象向 Publisher 提供您需要的任何内容,然后请求的取消在语义上更改为仅取消获取数据库,而不是取消和清理数据库连接。