在调用flatMap之前,对象被取消

时间:2018-02-25 07:31:51

标签: ios swift reactive-programming rx-swift

例如,有一个简单的任务可以进行网络调用,然后映射结果。

class Task {

    func execute() -> Single<String> {
        return doSomeRequest().flatMap { [weak self] data in
            if let string = self?.map(data: data) {
                return .just(string)
            }
            return .error(Error())
        }
    }

    func doSomeRequest() -> Single<Data> {
        // makes network call and returns Data 
    }

    func map(data: Data) -> String? {
        // maps Data to String 
    }
}

并且有使用此Task的函数:

func fetchSomeString() {
    Task().execute().subscribe(onSuccess: { string in
        print(string)
    }).disposed(by: disposeBag)
}

问题在于TaskdoSomeRequest()之前获得回复之前已被取消初始化,原因是weak self中的flatMap。其中一个解决方案是将Task实例存储为属性,但是假设我的情况没有位置。

是否有任何解决方案不会导致flatMap内存泄漏,但会正确执行Task

4 个答案:

答案 0 :(得分:1)

是&#39;一种解决方案是将Task实例存储为属性&#39;或者您可以在execute函数中传递/实质上存储实例。

static func execute(_ task: Task) -> Single<String> {
    return task.doSomeRequest().flatMap { data in
        if let string = task.map(data: data) {
            return .just(string)
        }
        return .error(Error())
    }
}

然后称之为:

Task.execute(Task()).subscri...

答案 1 :(得分:1)

您可以将map(data:)设为静态方法。这样您就不需要task实例,如果map实例没有使用任何实例成员,它可能比实例方法更有意义。

class Task {

    func execute() -> Single<String> {
        return doSomeRequest().map { [weak self] data in
            guard let string = Task.map(data: data) else {
                throw Error() 
            }
            return string
        }
    }

    func doSomeRequest() -> Single<Data> {
        // makes network call and returns Data 
    }

    static func map(data: Data) -> String? {
        // maps Data to String 
    }
}

我也通过调用Single.map并抛出错误对您进行了一些更改,这更易读。

答案 2 :(得分:1)

在我看来,您只需使用强引用self而不是弱引用。

我假设您的闭包被评估一次然后被丢弃。如果是这种情况,那么您将在操作结束时temporarily have a strong reference cycle that will self-break

答案 3 :(得分:1)

  

其中一个解决方案是将Task实例存储为属性,但是   让我们说我的情况没有地方。

有一个很棒的运算符可以将Task实例存储为属性,并且它不需要存储它的位置。该运算符名为Using

这是一个小例子:

flatMapLatest({ (room: String?) -> Observable<WebSocketServiceValue> in
    switch room {
    case .some(let room):
        return Observable
            .using({ WebSocketServiceInstance(room: room) }, observableFactory: { $0.observable })
    case .none:
        return Observable<WebSocketServiceValue>
            .just(WebSocketServiceValue.connected(false))
    }
})

我观察了一个可选String的房间。如果有一个有效的房间 - 我创建一个WebSocketServiceInstance并观察它的信号。更改房间后,WebSocketServiceInstanceflatMapLatest运营商而被取消。因此,只要您有连接,该实例就会存在。

在您的示例中,只要您使用Task运算符执行操作,您的Using类就可能存在。唯一需要的是 - Task类应该符合Disposable。但这很容易:

extension WebSocketServiceInstance: Disposable {

    func dispose() {
        socket?.delegate = nil
        socket?.close()
        socket = nil
    }
}