Today I would like to talk about working with threads in ReactiveCocoa. I will not go into the details of the framework framework and I believe that you are already familiar with the basic principles of reactive programming in iOS.
Working with threads in a mobile application is the most important topic and everyone knows that. The standard tools for this are GCD or NSOperation. But when using ReactiveCocoa in our project, everything becomes different. No, nobody forbids you to use standard tools, but why? Will we shove GCD into every block? To do this, ReactiveCocoa came up with a very convenient implementation.
To work with multithreading in ReactiveCocoa there is a class RACScheduler. In essence, this is a wrapper over GCD ... and it has the same thread priorities as GCD:
typedef enum : long { RACSchedulerPriorityHigh = DISPATCH_QUEUE_PRIORITY_HIGH, RACSchedulerPriorityDefault = DISPATCH_QUEUE_PRIORITY_DEFAULT, RACSchedulerPriorityLow = DISPATCH_QUEUE_PRIORITY_LOW, RACSchedulerPriorityBackground = DISPATCH_QUEUE_PRIORITY_BACKGROUND, } RACSchedulerPriority;
')
Consider the basic RACScheduler methods that we may need when working with it:
From the name, in principle, it becomes clear that the RACScheduler is returning to us, which will do the work in the main thread.
+ (RACScheduler *)mainThreadScheduler;
In this case, RACSCheduler with the specified priority is returned to us and is no longer in the main thread.
+ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority;
Returns a RACScheduler with priority RACSchedulerPriorityDefault.
+ (RACScheduler *)scheduler;
Returns the current RACScheduler from the current NSThread.
+ (RACScheduler *)currentScheduler;
A block that RACSSheduler can execute anywhere. And we will come back to this.
- (RACDisposable *)schedule:(void (^)(void))block;
The following are the main functions for RACSignal that we can use to control multi-threading:
This RACSignal method says that the blocks get new values in subscribeNext / doNext / subscribeError / etc. will be performed in the RACSCheduler, which we will return.
- (RACSignal *)deliverOn:(RACScheduler *)scheduler
This RACSignal method tells you which RACScheduler the block created during subscription creation will run in (if we are talking about ReactiveCocoa 2.5, this is: + [RACSignal createSignal:])
- (RACSignal *)subscribeOn:(RACScheduler *)scheduler
I will give two short examples and we’ll end it.
Create a simple signal:
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) {
Obviously, when creating a subscription to this signal, until the cycle ends, we will not receive a single value. Someone running the code in this block will be quite resource intensive. Let's try to spread by threads.
Create a subscription to the signal and point to the signal subscribeOn / deliverOn
[[[signal subscribeOn:[RACScheduler scheduler]] deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(id x) {
In this case, as can be seen from the comments, we will get the values in the main thread, where you can, for example, update the UI. And in the subscription creation block, the code will be executed in another thread, which will help reduce the load on the main thread.
And the last example, I will show you how to run code from the main thread in the main thread.
With GCD, it would look like everyone already knows this:
dispatch_async(dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_DEFAULT), ^{
And how can this be implemented with RACScheduler:
As we remember, when creating a subscription to this signal, we indicated that it will be executed not in the main thread. But what if in some place, we all need to run part of the code on the main thread? Very simple :) Here it will help us - (RACDisposable *) schedule: (void (^) (void)) block;
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) {