Ios 在创建可观察/单个时获取传递给subscribeOn()的调度程序

Ios 在创建可观察/单个时获取传递给subscribeOn()的调度程序,ios,swift,rx-swift,rx-cocoa,Ios,Swift,Rx Swift,Rx Cocoa,让我展示一个我正在努力解决的问题的简化示例: class CarService { func getCars() -> Single<[Car]> { return Single.create { observer in // Here we're using a thread that was defined in subscribeOn(). someCallbackToAPI { cars in

让我展示一个我正在努力解决的问题的简化示例:

class CarService {

    func getCars() -> Single<[Car]> {
        return Single.create { observer in
            // Here we're using a thread that was defined in subscribeOn().
            someCallbackToAPI { cars in
                // Here we're using main thread, because of the someCallbackToAPI implementation.
                observer(.success(cars))
            }
        }
    }
}

class CarRepository {

    func syncCars() -> Completable {
        return CarService().getCars()
            .flatMapCompletable { cars in
                // Here we're using main thread, but we want some background thread.
                saveCars(cars)
            }
    }
}

class CarViewController {

    func loadCar() {
        CarRepository().syncCars()
            .subscribeOn(someBackgroundScheduler)
            .observeOn(MainThread)
            .subscribe()
    }
}
class服务{
func getCars()->Single{
返回Single.create{observer in
//这里我们使用的线程是在subscribeOn()中定义的。
有人把汽车召回了
//这里我们使用的是主线程,因为有一些回调API实现。
观察员(.success(cars))
}
}
}
}
阶级继承{
func syncCars()->可完成{
return CarService().getCars()
.flatMapCompletable{车辆在
//这里我们使用的是主线程,但我们需要一些后台线程。
储蓄车
}
}
}
类CarViewController{
func装载车(){
CarRepository().syncCars()
.subscribeOn(someBackgroundScheduler)
.observeOn(主线程)
.subscribe()
}
}
从底部:
CarViewController
希望从一些外部API同步所有汽车。它定义了与
subscribeOn
同步时应使用的线程-我们不想阻止UI线程。不幸的是,在底层,
CarService
必须使用一些外部库方法(
someCallbackToAPI
),这些方法总是在主线程中返回结果。问题是,在收到结果后,在同一主线程中调用下面的所有方法,例如
saveCars
saveCars
可能会阻止UI线程,因为它会将数据保存到数据库中。当然,我可以在
CarService().getCars()
flatMapCompletable
之间的线程之间添加
observeOn
,但我希望
CarRepository
被转储,并且对线程一无所知。定义工作线程是CarViewController的责任


所以我的问题是,这是一种让调度器传入
subscribeOn
方法并在收到
someCallbackToApi
的结果后切换回调度器的方法吗?

简单的答案是否

正如您所猜测的,问题在于您的
someCallbackToAPI
正在路由到主线程,这不是您想要的,并且除了重新编写someCallbackToAPI之外,您无能为力。如果您使用的是Alamofire或Moya,我认为他们有其他方法不会在主线程上调用闭包,但我不确定。URLSession不会切换到主线程,因此可以使用它

如果希望
saveCars
发生在后台线程上,则必须使用observeOn将计算从main推回到后台线程上。
subscribeOn
将要做的唯一事情是在后台线程上调用
someCallbackToAPI(:)
,它无法指定函数将在哪个线程上调用其闭包

比如:

func syncCars() -> Completable {
    return CarService().getCars()
    .observeOn(someBackgroundScheduler)
        .flatMapCompletable { cars in
            // Now this will be on the background thread.
            saveCars(cars)
    }
}

最后,空订阅是一种代码味道。任何时候,如果您发现您的自呼
.subscribe()
不是出于测试目的,那么您很可能是做错了什么。

嗨,丹尼尔,谢谢您的回答。从架构的角度来看,决定在Alamofire/Moya级别上启动回调的线程并不是一个理想的解决方案。我能想到的唯一解决方案是将调度程序作为参数传递给
getCars
方法,并在启动回调后切换到它。顺便说一句,empty
subscribe()
只是一个例子。是的,但根本的问题是Alamofire在后台线程上执行工作,然后切换到主线程以发出结果,然后切换回后台线程以继续处理结果。最好一开始就不要涉及主线。如果Alamofire没有提供这样做的方法,那么就使用URLSession,URLSession是这样做的。