微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用Swift的Combine框架编写retryIf运算符

如何解决使用Swift的Combine框架编写retryIf运算符

我开始了解Swift + Swift的Combine框架,并想检查一下我实现retryIf(retries:,shouldRetry:)运算符的尝试是否有意义。特别是,我很好奇所有.erasetoAnyPublisher是否都是预期的/惯用的。

extension Publisher {
    func retryIf(retries: Int,shouldRetry: @escaping (Self.Failure) -> Bool) -> AnyPublisher<Self.Output,Self.Failure> {
        self.catch { error -> AnyPublisher<Self.Output,Self.Failure> in
            guard shouldRetry(error) && retries > 0 else {
                return Fail(error: error).erasetoAnyPublisher() 
            }
            return self.retryIf(retries: retries - 1,shouldRetry: shouldRetry).erasetoAnyPublisher()
        }.erasetoAnyPublisher()
    }
}

假设所有AnyPublisher都可以,那么何时要创建自己的Publisher结构?例如,常规的Combine运算符retry返回的是Retry<Upstream>结构而不是AnyPublisher,但是我想您可以按照与上面的代码相同的方式来实现它,例如:>

extension Publisher {
    func doOver(tries: Int) -> AnyPublisher<Self.Output,Self.Failure> in
            guard tries > 0 else { return Fail(error: error).erasetoAnyPublisher() }
            return self.doOver(tries: tries - 1).erasetoAnyPublisher()
        }.erasetoAnyPublisher()
    }
}

解决方法

您可以通过定义自己的eraseToAnyPublisher来消除最后的Publisher,从而消除所需的堆分配。例如:

extension Publisher {
    func retry(_ retries: Int,if shouldRetry: @escaping (Failure) -> Bool) -> MyPublishers.RetryIf<Self> {
        return .init(upstream: self,triesLeft: retries,shouldRetry: shouldRetry)
    }
}

enum MyPublishers { }

extension MyPublishers {
    struct RetryIf<Upstream: Publisher>: Publisher {
        typealias Output = Upstream.Output
        typealias Failure = Upstream.Failure

        init(upstream: Upstream,triesLeft: Int,shouldRetry: @escaping (Failure) -> Bool) {
            self.upstream = upstream
            self.triesLeft = triesLeft
            self.shouldRetry = shouldRetry
        }

        var upstream: Upstream
        var triesLeft: Int
        var shouldRetry: (Failure) -> Bool

        func receive<Downstream: Subscriber>(subscriber: Downstream) where Failure == Downstream.Failure,Output == Downstream.Input {
            upstream
                .catch {
                    triesLeft > 0 && shouldRetry($0)
                        ? Self(upstream: upstream,triesLeft: triesLeft - 1,shouldRetry: shouldRetry).eraseToAnyPublisher()
                        : Fail(error: $0).eraseToAnyPublisher()
                }
                .receive(subscriber: subscriber)
        }
    }
}

如果要消除eraseToAnyPublisher正文中的两个catch调用,则必须放弃使用catch。相反,您将必须实现自己的Subscription。实现Subscription要复杂得多,因为它必须是线程安全的。但是,catch主体内的那些调用只能在上游故障的情况下发生,并且每次故障仅发生一个调用。因此,如果上游故障很少发生,那可能就不值得付出努力。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。