Pass an Object of to an NSThread and keep sending it messages? - iphone

All examples/tutorials I can find regarding NSThreads and the only way I have ever dealt with them is using [NSThread detachNewThreadSelector:#selector(processDataMethod) toTarget:self withObject:nil]; to move all processing in a method to another thread sporting its own autoreleasePool etc.
This is good for "one shoot" operations that should just carry out and "disappear".
I have to make many small web service calls in bursts and I would like to create an NSThread object that stayed around for the duration of the applications run. Mostly to
prevent spawning many new threads when bursts occur.
I looked at initWithTarget and [NSThread start] which seems to deal with what I am trying to do. I am just not sure how put it together, does the thread go into my object, do I pass the object to my thread etc.
In a pseudo code / explanation way I am trying to implement this:
MyController;
#interface
NSThread *threadProperty; //a property, holding a thread I would like to message
MyWebService *webServiceObject; //the Class that holds web service methods e.g. - (NSArray*) searchFor:(NSString*) searchStr;
on viewDidLoad:
instantiate webServiceObject;
instantiate threadProperty;
hand threadProperty the webServiceObject.
start the thread. //make it go forever with [[NSRunLoop currentRunLoop] run];
on user does something:
tell the thread to tell the webServiceObject to searchFor:#"Foo";
on thread has a response:
thread says webServiceObject says there are 19 #"Foo" up in the cloud.
I have been fiddling around with how to make it work, but realizes that I don't understand
the subject well enough to come up with a "pattern" to implement it.
I guess I am trying to implement a cluster of method calls on an object living in a separate continuous thread, instead of spawning a thread each time I need a particular method called (potentially 8 times in a row within 500 ms. and then not at all for 2 minutes).
I hope it makes sense and that someone a bit more experienced in threads can point me in the right direction.
This post is pretty close to what I have tried: how to use performSelect...

Related

iOS : Best way to organise multithreading

Guys I need some help to architect my multithreading in iOS.
I'm using ARC in my code.
So basically I need following,
In my main thread nstimer fire some method which should be executed in a separate thread, that thread does some calculation and puts data into some ivar, and another thread should read data from that ivar and do some other calculation, i.e. if there is no data the second thread should wait until there is any.
So basically I would like to hear some advice which technology is the best choice for my task, to use cocoa thread (NSThread), GCD or Operation queues.
Also can someone please provide me with some pseudo code on aspects of mutual blocking/synchronization between two threads.
Since you are saying that some calculations should wait for other calculations to finish, I would say that you should have a look at NSOperation and set dependencies for the different operations (using addDependency).
Unless you left something our of your problem description, that is a perfect fit for GCD/block combo. In fact, I wouldn't even use a NSTimer (GCD provides a better alternative - see dispatch_source_create for example of creating GCD based timer), but that's your call, and not what the question asked. Anyway, with GCD...
- (void)handleTimer:(NSTimer *)timer {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
__block id someObject;
// Do work... manipulate someObject in some manner...
// When done, invoke other thread... main thread in this case
dispatch_async(dispatch_get_main_queue(), ^{
// This code is running in a different thread, and can use someObject directly
});
});
}

iOS -- persuade an NSOperation to yield to other threads before starting a long and apparently unbreakable task?

I have a low-priority PDF-drawing NSOperation. I am using the function CGContextDrawPDFPage to draw the page.
As best I can tell, the app will not switch to another thread while the CGContextDrawPDFPage function is executing. I suppose I need to break up my graphics context into several smaller contexts to get around that. But I still have one problem -- how can I persuade the operation to allow other threads to perform their tasks before starting that draw? The way things are set up right now, the app draws the page right away, even though the thread priority is set to zero.
Here is the code that adds the NSOperation to the queue:
MyNSOperationSubclass* op = [[MyNSOperationSubclass alloc]initWithStuff: stuff];
[op setThreadPriority:0.];
[self.drawingQueue addOperation:op];
Here is some code within MyNSOperationSubclass:
-(void) drawStuff {
NSLog(#"drawStuff");
#autoreleasepool {
// some code to setup the graphics context and PDF page here
CGContextDrawPDFPage(context, page);
[self finish]; // finishes the NSOperation
}
}
-(void) main {
NSLog(#"main");
#autoreleasepool {
[self drawStuff];
}
}
-(void) start {
self.isExecuting = YES;
[NSThread detachNewThreadSelector:#selector(main) toTarget:self withObject:nil];
}
what happens is that even though the thread priority is set to zero, the setup code is so quick that the NSOperation gets into the CGContextDrawPDFPage call right as the operation is started. But once that call has started, the thread won't yield to other threads until it has finished. So the drawing operation happens right away, which is the opposite of what I want to do.
EDIT: After taking a closer look, the first answerer is correct that the CGContextDrawPDFPage call does not always prevent a thread from yielding. But even with a non-concurrent operation, I still have the problem that it finishes quickly, rather than waiting around for other, higher-priority stuff to happen.
EDIT 2: Apparently I don't understand how thread priorities work. After changing it to a nonconcurrent operation, the thread priority of the operation thread is 0.5, even though I called [op setThreadPriority: 0] before adding it to the queue.
So, the first thought is that your thread can't avoid "yielding" to other threads. Context switching is pre-emptive here. So you might want to talk about why you think this thread is not yielding. You may have some other bigger issue here with the other work that you are expecting to be done.
Second, when you set the thread priority for this operation, you're not setting the priority of this new thread that you are creating yourself here:
[NSThread detachNewThreadSelector:#selector(main) toTarget:self withObject:nil];
That's a new thread with normal priority.
But stepping back from that a second, why not just make this a non-concurrent operation and let your NSOperationQueue manage the thread for you? The queue will create a new thread for you as needed (and apply your thread priority that you asked for).
Alternatively, unless you still need to support iOS3, you can just use the global low priority GCD queue for this work (or target your own GCD queue to that global queue if you need finer control over order and still want all of this done with low priority).
So, consider letting either NSOperationQueue or GCD manage some of this for you. And think about why you think this thread is interfering with other work. That's probably some bigger issue.
Hope that helps.

Main thread's execution context

I need ideas on the following -
In the main thread at some point of execution say Point A(sequential logic), I need to remember the state of execution and delegate the execution of some other logic onto another thread, and let the main thread handle the UI events etc. When the delegated logic completes on the other thread then the flow of execution should continue from the point A and should recollect the entire execution context and proceed as if it never paused there.
Regards,
Sunil Phani Manne
It's hard to implement this exactly the way you're saying (for example do(things)... yield(other_thread); ...do(more_things);.
Here are a couple other options I can think of (you'd have to implement these yourself, using delegates or notifications for example; I'm just giving a basic outline of how it would work):
do(things)
[object doStuffOnOtherThreadWithCallback:^{ // block-based
do(more_things)...
}];
or...
do(things)
[object doStuffOnOtherThreadWithCallbackTarget:self // target/selector-based
selector:#selector(callbackSelector)];
}
- (void)callbackSelector {
do(more_things)...
}
One option you have is encapsulating the whole sequential logic that comes after Point A in your delegate and then execute it on the main thread when the secondary thread ends.
In other words, when you start the thread by calling, e.g.
[NSThread detachNewThreadSelector:sel toTarget:target withObject:delegate]
you can implement your target target so that it has a specific selector completion that is called at the end of sel on the main thread, like this (this is the your delegate class):
#implementation YOURDelegateClass {
.....
-(void)completion {
}
-(void)sel {
...
...
[self performSelectorOnMainThread:#selector(#"completion") withObject:self];
}
}
Of course you have many sub-options available here, like using a different call to start the background execution, etc.
The important point is that: you have to encapsulate in a selector all the logic that comes after Point A, and that you have to schedule the execution of this selector on the main thread, in order to get back to your context (although your context will have changed in the meantime because you will also have updated the UI).
EDIT:
Having to schedule the execution on the main thread defeats blocks from being suitable for this kind of callback. On the other side, block have the advantage that they in some limited sense give you access to the same lexical context in which the block was defined (which is roughly what you call context).
A workaround for this could be the following. Before detaching the new thread, store in a delegate the block you would like to execute at completion:
typedef void(^CustomBlock)(void);
#property (nonatomic, copy) CustomBlock customBlock;
....
int a = ...
delegate.customBlock = ^{
NSLog(#"hello %d.....", a);
}
[NSThread detachNewThreadSelector:sel...
....
-(void)completion {
[self customBlock];
}
Of course, you only get the context preservation that is guaranteed to you by block. But here you hit against a limit of the language.
If you need more context preservation, then the only possibility is encapsulating that context in your delegate class ivars.
One thing is for sure. There, most probably, isn't any direct feature in Cocoa that does that. Since you're saying that you can't duplicate the resources onto the new thread (for a very good reason), I am going to suggest that you make use of NSUndoManager. For every change you make in the thread, push an undo operation for that change onto the undo manager. At the end of the thread, execute all the undo operations in the undo manager object. This should, if done correctly, restore your state. Now, since the idea is untested, there could be a chance that not all actions can be undone. You will have to check that out first.

Grand Central Dispatch (GCD) vs. performSelector - need a better explanation

I've used both GCD and performSelectorOnMainThread:waitUntilDone in my apps, and tend to think of them as interchangeable--that is, performSelectorOnMainThread:waitUntilDone is an Obj-C wrapper to the GCD C syntax. I've been thinking of these two commands as equivalent:
dispatch_sync(dispatch_get_main_queue(), ^{ [self doit:YES]; });
[self performSelectorOnMainThread:#selector(doit:) withObject:YES waitUntilDone:YES];
Am I incorrect? That is, is there a difference of the performSelector* commands versus the GCD ones? I've read a lot of documentation on them, but have yet to see a definitive answer.
As Jacob points out, while they may appear the same, they are different things. In fact, there's a significant difference in the way that they handle sending actions to the main thread if you're already running on the main thread.
I ran into this recently, where I had a common method that sometimes was run from something on the main thread, sometimes not. In order to protect certain UI updates, I had been using -performSelectorOnMainThread: for them with no problems.
When I switched over to using dispatch_sync on the main queue, the application would deadlock whenever this method was run on the main queue. Reading the documentation on dispatch_sync, we see:
Calling this function and targeting
the current queue results in deadlock.
where for -performSelectorOnMainThread: we see
wait
A Boolean that specifies whether the
current thread blocks until after the
specified selector is performed on the
receiver on the main thread. Specify
YES to block this thread; otherwise,
specify NO to have this method return
immediately.
If the current thread is also the main
thread, and you specify YES for this
parameter, the message is delivered
and processed immediately.
I still prefer the elegance of GCD, the better compile-time checking it provides, and its greater flexibility regarding arguments, etc., so I made this little helper function to prevent deadlocks:
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
Update: In response to Dave Dribin pointing out the caveats section ondispatch_get_current_queue(), I've changed to using [NSThread isMainThread] in the above code.
I then use
runOnMainQueueWithoutDeadlocking(^{
//Do stuff
});
to perform the actions I need to secure on the main thread, without worrying about what thread the original method was executed on.
performSelectorOnMainThread: does not use GCD to send messages to objects on the main thread.
Here's how the documentation says the method is implemented:
- (void) performSelectorOnMainThread:(SEL) selector withObject:(id) obj waitUntilDone:(BOOL) wait {
[[NSRunLoop mainRunLoop] performSelector:selector target:self withObject:obj order:1 modes: NSRunLoopCommonModes];
}
And on performSelector:target:withObject:order:modes:, the documentation states:
This method sets up a timer to perform the aSelector message on the current thread’s run loop at the start of the next run loop iteration. The timer is configured to run in the modes specified by the modes parameter. When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in one of the specified modes; otherwise, the timer waits until the run loop is in one of those modes.
GCD's way is suppose to be more efficient and easier to handle and is only available in iOS4 onwards whereas performSelector is supported in the older and newer iOS.

Threading, priorities, and abandonment

I have recently become thread curious on iOS. Please point me in the direction you would take, to achieve (if possible) the following on modern iOS devices... thank you!
The user is typing in text, say a word every few seconds.
From time to time I want to launch DifficultProcess to do some semantic processing. In short, I guess I need to be able to do four things:
launch DifficultProcess from main
if DifficultProcess completes, get a message back from it to the same main
abandon, get rid of, DifficultProcess if I want to, from main
and finally the priority question: DifficultProcess must have much lower priority than main or user input, I want DifficultProcess to have really really looow priority; is that even possible?
What, essentially, are the calls one uses for A, B, C in modern (2011) (late January) iOS? I don't care about Dad's methods! And is "D" even possible in any way?
I guess those are the four ideas!
So in particular I want to send a message to, in other words call a routine in, the running background process (in that way, one could kill off the running background process if desired, or perhaps change it's mode of operation etc).
(For anyone born before 1997, you will recognise that as a typical "speculative processing" paradigm.)
Thanks for pointers for anyone who can be bothered on this!
I would recommend using NSOperation and NSOperationQueue to manage background activity that you need to be able to cancel arbitrarily.
NSOperation's -cancel and NSOperationQueue's -cancelAllOperations are the methods to look at.
To get messages back from the background to the main thread, the dispatch_async-to-main-thread-queue technique is fine. You can combine this with a delegate protocol for your NSOperation to codify the messages you want to send back.
E.g.
#protocol MyOperationDelegate
- (void) operationStarted:(MyOperation *)operation;
- (void) makingProgressOnItem:(id)anItem otherInterestingItem:(NSDictionary *)otherItem remainingCount:(NSUInteger)count;
- (void) operationWillFinish:(MyOperation *)operation;
#end
#interface MyOperation
id <MyOperationDelegate> delegate;
#end
#implementation MyOperation
...
- (void) cancel
{
[super cancel];
// Tell the delegate we're about to finish (due to cancellation).
dispatch_sync (dispatch_get_main_queue(), ^{
[self.delegate operationWillFinish:self];
});
}
- (void) main
{
// Check for cancellation
if (self.isCancelled) return;
// Starting
dispatch_sync (dispatch_get_main_queue(), ^{
[self.delegate operationStarted:self];
});
if (self.isCancelled) return; // Another cancel check
// Send async progress messages periodically while doing some work
while (workNotDone)
{
// Do some work ...
dispatch_async (dispatch_get_main_queue(), ^{
[self.delegate makingProgressOnItem:foo otherInterestingItem:bar remainingCount:baz];
});
if (self.isCancelled) return;
}
// About to finish
if (!self.isCancelled) {
dispatch_sync (dispatch_get_main_queue(), ^{
[self.delegate operationWillFinish:self];
});
}
}
#end
KVO is no good for interthread communication; the observation is received on the thread that originates the key value change. So, if your background thread changes a value, your background thread is going to receive the KVO about it. Probably not what you want.
Grandpa's -performSelectorOnMainThread:withObject:waitUntilDone: continues to be a fine way to get messages back to the main thread. The limitation is that your messages can only access one object-based argument. The dispatch_async to the main thread doesn't have this limitation.
If you want to fire off an asynchronous (or synchronous) NSNotification's from a background thread to the main thread, you need to use -performSelectorOnMainThread.
NSNotification *note = [NSNotification notificationWithName:FinishedABunchOfWorkNotification object:self userInfo:nil];
[[NSNotificationCenter defaultCenter] performSelectorOnMainThread:#selector(postNotification:) withObject:note waitUntilDone:YES];
I would suggest using dispatch_async to the global low priority queue (dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)).
Cancellation is trickier though. There's no good general mechanism for canceling background work that I'm aware of aside from "chunking" it and checking a flag each chunk
To get messages back just dispatch_async back to the main queue. If you squint just right you can think of dispatch_async as "send message" in an actor model.
(edit) if you need serialization of stuff in the background, make a private queue and set its target to the global low priority one, iirc.
At the risk of quoting Dad's method (it has been around since iPhone version 2) I use
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg
It's easy and foolproof as long as you remember that you must create a new autorelease pool in the method you pass as selector, and drain it at the end of the method. Apart from that do whatever you like - EXCEPT touch UIKit. It isn't thread-safe so any UI changes must be done through
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
or KVO triggers. Key Value Observing would be a good way for your background thread to communicate to your main thread that the work is done.
- (void)myBackgroundThreadMethod {
NSAutoreleasePool *threadPool = [[NSAutoreleasePool alloc] init];
// my time-consuming processing here
[threadPool drain];
}
For more precise control of threads you need to look at NSThread. Threading Programming Guide lays it all out in detail - if you create a thread through NSThread then you have control over when the thread is started. The document does recommend leaving the thread alone and just letting it terminate - but shows how you can terminate it. One way is - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait
NSThread docs also say "leave priority alone". You can set thread priority with
+ (BOOL)setThreadPriority:(double)priority
but I've never known it to be necessary, the scheduler is smart enough to maintain UI responsiveness.