如何在 Swift Combine Sink 收到至少一个值后停止存储 AnyCancellable?

How to stop storing AnyCancellable after Swift Combine Sink has received at least one value?

我有一个接收器需要在收到第一个值后立即取消。我不关心未来的价值,只关心第一个发布的价值。因为不存储sink创建的AnyCancelable,垃圾回收器会删除sink,我必须存储它。同时我也必须在sink完成后进行清理,否则我会内存泄漏。

我使用 UUID → AnyCancelable 映射构建了一个,但我担心这比应该的更复杂;还有另一种方法吗? Combine 推荐什么?

@Published var locationState: (location: CLLocation?, error: Error?)?
var requestLocationSinks: [String: AnyCancellable] = [:]

// 1. Generate ID to uniquely identify the current sink.
let sinkID = UUID().uuidString

// 2. Start the sink and store it in our ID → AnyCancellable dictionary.
requestLocationSinks[sinkID] = $locationState.sink { locationState in
    if let locationState = locationState {
        invokeCallbackWithLocationState(locationState)
    }
    // 3. Remove the stored AnyCancellable as soon as we received our first value!
    self.requestLocationSinks.removeValue(forKey: sinkID)
}

如果你只需要让它一直存活到sink被调用一次,你可以只创建一个临时变量

var cancellable: AnyCancellable?
cancellable = $locationState.sink { locationState in
    if let locationState = locationState {
        invokeCallbackWithLocationState(locationState)
    }
    cancellable = nil
}

这将保留 AnyCancellable 足够长的时间(因为闭包保留了引用)

如果您只对上游的第一个元素感兴趣,您可以使用运算符 .first() 或运算符 .prefix(1)。他们是平等的。使用 .prefix(n) 您可以定义传递的上游元素的数量。

myPublisher
     .first()
     .sink( ... )

or

myPublisher
     .prefix(1 or n)
     .sink( ... )

请注意,上面的两个运算符会导致 .finished 完成。这意味着订阅将在操作后被删除。

publisher is triggered -> receiveValue -> receiveCompletion (finished) -> subscription is deleted

如果您使用 AnyCancellable 并想删除您的订阅,您应该使用 AnyCancellable .cancel() 功能。

var cancellable: AnyCancellable?
cancellable = myPublisher
     .sink(receiveValue: { value in 
          // Your logic.
          cancellable.cancel()
     })

我想在您的情况下,运算符 .first() 正是您所需要的。