Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

scheduleInRunLoop - threading network connections

I've not found any decent documentation that explains the threading process for NSStream. To be specific, let's go for NSInputStream. Threading in Objective-C to me is currently a mystery simply because it appears to be so simple.

What my question is refers to this line primarily:

[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

You can specify the run loop that the input stream will run in, which I thought was quite cool. The thing is, if I want the input and output streams to run in their own threads, and both are instantiated in a single class, say Connection, then how do you get them to run in their own threads?

The reason I ask is because of delegates. Previously we would've done [inputStream setDelegate:self] which means we have to declare stream:handleEvent to handle incoming/outgoing data.

So ultimately my question is, if you have one class which sets up the input and output stream, how do you both thread each stream and delegate responsibility for handling stream events to the current class?

Here's some code to chomp on:

[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];

I'm thinking the following:

  • You can't delegate responsibility for both threads in the current class, you'd have to delegate to separate objects.
  • One thread would do for both streams? (I don't personally think so, because input/output will run concurrently)
  • I'm thinking this through wrong, and you can create a separate run loop and call scheduleRunLoop against some separate thread?

Any ideas?

like image 265
Kezzer Avatar asked Jul 28 '12 08:07

Kezzer


1 Answers

Note the differences between heap and stack.

Each thread has its own stack but all threads access the same heap.

The input stream needs its own thread because when reading the stream without reaching any EOF, the thread blocks if no new characters come and therefore waits. So to prevent your application from blocking the input stream needs a separate thread.

Continuing since delegates can't be static methods you have to copy or at least synchronise the use of a buffer to return results. Remember each thread has its own stack but both access to the same heap.

The NSStreamDelegate is an interface that allows you to specify who will manage events from a stream. Therefore allowing you to split programming for the handling of streams and the handling of their events. As your can think of delegates as pointers to functions you have to make sure they exist at runtime, which is why they are usually used and defined with protocols. But calling a method of a delegate does not mean you call a function of another thread. You only apply parameters/objects to a method of another object/class that will/must exist at runtime.

Apples NSThread class and NSRunloop class make it easy but confuse because a runloop is not the same as a thread. Each thread can have a runloop which is at least looped once and return immediately when nothing else is there to do. With [NSRunLoop currentRunLoop] you are asking for the runloop of the thread you are in, you are not creating another thread. So calling for the very same runloop from the same thread twice results in work in the same thread. Following this means if one part is blocking, you are blocking the thread so the other parts in the same thread wait also. (Where in the last sentence you could exchange the word thread with runloop and its still the same, its is blocking the thread)

As a matter of simultaneousness usually more than one socket port is involved when input and output streams should work at the 'same' time. NSInputStream is read -only & NSOutputStream is write-only. Where it is wise to give the input stream its own thread for reasons of possibly unexpected results from data and timing given by the nature of a remote sender which is out of your control. You are responsible to define if a runloop (respective thread) should stay active after invoking once.

Doing so your output stream is then in an/the other thread, the one you asked for its current runloop. You can not create or manage runloops, you just ask for it because each thread has one and if not - one is created for you.

On iOS you have a bunch of solutions available to implement your personal design pattern for simultan input and output streams. You could go with NSThread also with -performSelectorInBackground:SEL withObject:(nullable Id) which is actually an extension of NSObject defined in NSThread.h. But the last one does not allow you to define a special run-mode.

If you do not want to create a costume NSThread subclass to your needs here is a simple solution mentioned that may also work for you. iOS how can i perform multiple NSInputStream Here [NSRunLoop currentRunloop] is the runloop of the thread that is detached. It is also possible to detach a new thread with a block

id<NSStreamDelegate> streamDelegate = //object that conforms to the protocol
[NSThread detachNewThreadWithBlock:^(void){
    NSInputStream *inputStream;
    [inputStream setDelegate:streamDelegate];
    // define your stream here
    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                   forMode:NSRunLoopCommonModes];
    [inputStream open];
}];`

PS: the example from @Ping is a switch to interact on events of both streams, it does not make streaming in & out simultaneous. Well, but you could use the example on both streams and their events no matter if they are simultaneous or not, typical NSStreamDelegate stuff.

like image 90
Ol Sen Avatar answered Sep 27 '22 18:09

Ol Sen