Combine:如何在取消 AnyCancellable 时清理资源?

Combine: How to clean up resources while an AnyCancellable is being cancelled?

概览:

问题:

目的:

比如我想用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 提供您需要的任何内容,然后请求的取消在语义上更改为仅取消获取数据库,而不是取消和清理数据库连接。