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?
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.
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {
case NSStreamEventNone:
break;
case NSStreamEventOpenCompleted:
break;
case NSStreamEventHasBytesAvailable:
[self _readData];
break;
case NSStreamEventHasSpaceAvailable:
[self _writeData];
break;
case NSStreamEventErrorOccurred:
break;
case NSStreamEventEndEncountered:
break;
default:
break;
}
}
Related
I'm trying to figure out how NSRunLoop works.
So there are a few delayed tasks and I want to perform them in a few seconds using NSRunLoop. And I want to create NSRunLoop manually. How am I supposed to do this?
NSRunLoop *loop = [NSRunLoop currentRunLoop];
//create delayed tasks
[object performSelector:NSSelectorFromString(#"firstMethod") withObject:firstArgument afterDelay:5.0];
[object performSelector:NSSelectorFromString(#"secondMethod") withObject:secondArgument afterDelay:3.0];
//and here I must run a loop
while(flag&&[loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
And also I have somehow to stop this loop. As you see I'm totally confused and lots of Apple's documentation and topics here didn't help me.
To the Q and the comments:
Of course, they are never performed. -performSelector:… is attached to the thread's current run loop. This run loop is never reached, because your program get stuck in your private run loop.
Moreover, it is simply not possible, to attach a run loop to a thread:
Your application cannot either create or explicitly manage NSRunLoop objects. Each NSThread object, including the application’s main thread, has an NSRunLoop object automatically created for it as needed. If you need to access the current thread’s run loop, you do so with the class method currentRunLoop.
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSRunLoop_Class/index.html
I'm trying to understand Operations and Threads better, and looked to AFNetworking's AFURLConnectionOperation subclass for example, real-world, source code.
My current understanding is when instances of NSOperation are added to an operation queue, the queue, among other things, manages the thread responsible for executing the operation. In Apple's documentation of NSOperation it points out that even if subclasses return YES for -isConcurrent the operation will always be started on a separate thread (as of 10.6).
Based on Apple's strong language throughout the Thread Programming Guide and Concurrency Programming Guide, it seems like managing a thread is best left up to the internal implementation of NSOperationQueue.
However, AFNetworking's AFURLConnectionOperation subclass spawns a new NSThread, and the execution of the operation's -main method is pushed off onto this network request thread. Why? Why is this network request thread necessary? Is this a defensive programming technique because the library is designed to be used by a wide audience? Is it just less hassle for consumers of the library to debug? Is there a (subtle) performance benefit to having all networking activity on a dedicated thread?
(Added Jan 26th)
In a blog post by Dave Dribin, he illustrates how to move an operation back onto the main thread using the specific example of NSURLConnection.
My curiosity comes from the following section in Apple's Thread Programming Guide:
Keep Your Threads Reasonably Busy.
If you decide to create and
manage threads manually, remember that threads consume precious system
resources. You should do your best to make sure that any tasks you
assign to threads are reasonably long-lived and productive. At the
same time, you should not be afraid to terminate threads that are
spending most of their time idle. Threads use a nontrivial amount of
memory, some of it wired, so releasing an idle thread not only helps
reduce your application’s memory footprint, it also frees up more
physical memory for other system processes to use.
It seems to me that AFNetworking's network request thread isn't being "kept reasonably busy;" it's running an infinite while-loop for handling networking I/O. But, see, that's the point of this questions - I don't know and I am only guessing.
Any insight or deconstruction of AFURLConnectionOperation with specific regards to operations, threads (run loops?), and / or GCD would be highly beneficial to filling in the gaps of my understanding.
Its an interesting question and the answer is all about the semantics of how NSOperation and NSURLConnection interact and work together.
An NSURLConnection is itself an asynchronous task. It all happens in the background and calls its delegate periodically with the results. When you start an NSURLConnection it schedules the delegate callbacks using the runloop it is scheduled on, so a runloop must always be running on the thread you are executing an NSURLConnection on.
Therefore the method -start on our AFURLConnectionOperation will always have to return before the operation finishes so it can receive the callbacks. This requires that AFURLConnectionOperation be an asynchronous operation.
from: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOperation_class/index.html
The value of property is YES for operations that run asynchronously with respect to the current thread or NO for operations that run synchronously on the current thread. The default value of this property is NO.
But AFURLConnectionOperation overrides this method and returns YES as we would expect. Then we see from the class description:
When you call the start method of an asynchronous operation, that method may return before the corresponding task is completed. An asynchronous operation object is responsible for scheduling its task on a separate thread. The operation could do that by starting a new thread directly, by calling an asynchronous method, or by submitting a block to a dispatch queue for execution. It does not actually matter if the operation is ongoing when control returns to the caller, only that it could be ongoing.
AFNetworking creates a single network thread using a class method that it schedules all the NSURLConnection objects (and their resulting delegate callbacks) on. Here is that code from AFURLConnectionOperation
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
#autoreleasepool {
[[NSThread currentThread] setName:#"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:#selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
Here is code from AFURLConnectionOperation showing them scheduling the NSURLConnection on the runloop of the AFNetwokring thread in all runloop modes
- (void)start {
[self.lock lock];
if ([self isCancelled]) {
[self performSelector:#selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
} else if ([self isReady]) {
self.state = AFOperationExecutingState;
[self performSelector:#selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
}
[self.lock unlock];
}
- (void)operationDidStart {
[self.lock lock];
if (![self isCancelled]) {
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
for (NSString *runLoopMode in self.runLoopModes) {
[self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
[self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
}
//...
}
[self.lock unlock];
}
here [NSRunloop currentRunloop] retrieves the runloop on the AFNetworking thread instead of the mainRunloop as the -operationDidStart method is called from that thread. As a bonus we get to run the outputStream on the background thread's runloop as well.
Now AFURLConnectionOperation waits for the NSURLConnection callbacks and updates its own NSOperation state variables (cancelled, finished, executing) itself as the network request progresses. The AFNetworking thread spins its runloop repeatedly so that as NSURLConnections from potentially many AFURLConnectionOperations schedule their callbacks they are called and the AFURLConnectionOperation objects can react to them.
If you always plan to use queues to execute your operations, it is simpler to define them as synchronous. If you execute operations manually, though, you might want to define your operation objects as asynchronous. Defining an asynchronous operation requires more work, because you have to monitor the ongoing state of your task and report changes in that state using KVO notifications. But defining asynchronous operations is useful in cases where you want to ensure that a manually executed operation does not block the calling thread.
Also note that you can also use an NSOperation without a NSOperationQueue by calling -start and observing it until -isFinished returns YES. If AFURLConnectionOperation was implemented as a synchronous operation and blocked the current thread waiting for NSURLConnection to finish it would never actually finish as NSURLConnection would schedule its callbacks on the current runloop, which wouldn't be running as we would be blocking it. Therefore to support this valid scenario of using NSOperation we have to make the AFURLConnectionOperation asynchronous.
Answers to Questions
Yes, AFNetworking creates one thread which it uses to schedule all connections. Thread creation is expensive. (this is partly why GCD was created. GCD keeps a pool of threads running for you and dispatches blocks on the different threads as needed without having to create, destroy and manage threads yourself)
The processing is not done on the background AFNetworking thread. AFNetworking uses the completionBlock property of NSOperation to do its processing which is executed when finished is set to YES.
The exact execution context for your completion block is not guaranteed but is typically a secondary thread. Therefore, you should not use this block to do any work that requires a very specific execution context. Instead, you should shunt that work to your application’s main thread or to the specific thread that is capable of doing it. For example, if you have a custom thread for coordinating the completion of the operation, you could use the completion block to ping that thread.
the post processing of HTTP connections is handled in AFHTTPRequestOperation. This class creates a dispatch queue specifically for transforming response objects in the background and shunts the work off onto that queue. see here
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
self.completionBlock = ^{
//...
dispatch_async(http_request_operation_processing_queue(), ^{
//...
I guess this begs the question could they have written AFURLConnectionOperation to not make a thread. I think the answer is yes as there is this API
- (void)setDelegateQueue:(NSOperationQueue*) queue NS_AVAILABLE(10_7, 5_0);
Which is meant to schedule your delegate callbacks on a specific operation queue as opposed to using the runloop. But as we're looking at the legacy part of AFNetworking and that API was only available in iOS 5 and OS X 10.7. Looking at the blame view on Github for AFURLRequestOperation we can see that mattt actually wrote the +networkRequestThread method coincidentally on the actual day the iPhone 4s and iOS 5 were announced back in 2011! As such we can reason that the thread exists because at the time it was written we can see that making a thread and scheduling your connections on it was the only way to receive callbacks from NSURLConnection in the background while running in an asynchronous NSOperation subclass.
the thread is created using the dispatch_once function. (see the extra code snipped i added as you suggested) This function ensures that the code enclosed in the block it runs will be run only once in the lifetime of the application. The AFNetworking thread is created when it is needed and then persists for the lifetime of the application
When i wrote NSURLConnectionOperation i meant AFURLConnectionOperation. I corrected that thanks for mentioning it :)
In my application at some point I have a bunch of messages scheduled using performSelector.
Under some conditions, while handling an UI action, I need to wait for all the currently scheduled selectors to fire.
I could place my code in another method and schedule it using performSelector:target:argument:order:modes: with order value high enough to be sure it will fire last, but there are reasons why I think that would make an ugly solution.
So I send [[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]] and it seems to work just like I need it to.
Still, I'm not sure if that is a legitimate way to let the run loop roll for just one iteration. Also, are there any potential troubles associated with this approach?
Okay, answering my own question.
First of all it's a duplicate (also, this).
Next, generally, sending [[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]] from within the same run loop is a bad idea:
In theory, autorelease pool will get drained that way. In practice, I've not been able to make my app crash by using objects allocated pre-runUntilDate (under ARC), but better not to risk anyway.
If somehow another action gets dequeued during that runUntilDate it might cause some unexpected side effects.
TL;DR I should do myself a favor and replace that piece of code with something more deterministic.
I'm writing a multi threaded application for iOS.
I am new to Objective-C, so I haven't played around with threads in iPhone before.
Normally when using Java, I create a thread, and send "self" as object to thread. And from the thread I can therefor call the main thread.
How is this done in Objective C?
How can I call the main thread from a thread? I have been trying with NSNotificationCenter, but I get a sigbart error :/
This is how the thread is started:
NSArray *extraParams = [NSArray arrayWithObjects:savedUserName, serverInfo, nil]; // Parameters to pass to thread object
NSThread *myThread = [[NSThread alloc] initWithTarget:statusGetter // New thread with statusGetter
selector:#selector(getStatusFromServer:) // run method in statusGetter
object:extraParams]; // parameters passed as arraylist
[myThread start]; // Thread started
activityContainerView.hidden = NO;
[activityIndicator startAnimating];
Any help would be great!
you accomplish this by adding a message to the main thread's run loop.
Foundation provides some conveniences for this, notably -[NSObject performSelectorOnMainThread:withObject:waitUntilDone:] and NSInvocation.
using the former, you can simply write something like:
[self performSelectorOnMainThread:#selector(updateUI) withObject:nil waitUntilDone:NO]
a notification may be dispatched from a secondary thread (often the calling thread).
You can either use performSelectorOnMainThread:withObject:waitUntilDone: or, if you're targeting iOS 4 and later, Grand Central Dispatch, which doesn't require you to implement a method just to synchronize with the main thread:
dispatch_async(dispatch_get_main_queue(), ^ {
// Do stuff on the main thread here...
});
This often makes your code easier to read.
Though this is not a direct answer to your question I would highly recommend you take a look at Grand Central Dispatch. It generally gives better performance than trying to use threads directly.
As Justin pointed out, you can always perform a function in the main thread by calling performSelectorOnMainThread, if you really needed too.
I have created an NSMetadataQuery to search for all audio available through Spotlight, modelled on the following command, which returns plenty of results:
mdfind kMDItemContentTypeTree == "public.audio"
Here is the code I'm using:
NSMetadataQuery * q = [[[NSMetadataQuery alloc] init] autorelease];
[q setPredicate:[NSPredicate predicateWithFormat:#"kMDItemContentTypeTree == 'public.audio'", nil]];
NSLog(#"%#", [[q predicate] predicateFormat]);
if ([q startQuery])
while ([q isGathering]) {
NSLog(#"Polling results: %i", [q resultCount]);
[NSThread sleepForTimeInterval: 0.1];
}
[q stopQuery];
}
For some reason, the query seems to remain in the gathering phase indefinitely, and never gets a single result. I would like to know why this is the case, and whether there would be a more elegant way to block the thread while waiting for a result, preferably avoiding polling.
My application is actually not based on Cocoa but on NSFoundation, and thus far has no event loop. I realize that the conventional approach to dealing with Spotlight queries is to subscribe to an event notification, but I don't know how to block while waiting for one, and that approach seems a little overkill for my purposes.
To phrase my question as simply as possible, can I block my thread while waiting for the NSMetadataQuery to conclude the initial gathering phase? If so, how?
Instead of [NSThread sleepForTimeInterval:0.1] try:
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
The former is actually stopping the thread altogether, which means the query can't be running. The latter is kind of like sleeping, except that it also allows event sources to fire.