I’m an experienced C/C++ programmer coming up to speed on Objective C on the iPhone. I have done a lot of searching, but haven’t found a satisfactory answer on what must be a common question; I apologize if this is answered elsewhere, pointers would be appreciated.
My app is very CPU intensive. The UI has a simple display that shows progress and a start/stop button. What is the best way to allocate the most possible CPU cycles to getting the work done, while still ensuring that the display is updated regularly and the start/stop button is responsive? I have read that you should not do work in the main thread, but beyond that I haven’t found many suggestions. In light of this I have implemented my work in an NSOperation queue. I have also put the screen refresh in its own queue. I have also liberally sprinkled the code with NSThread sleepForTimeIntervals. I have experimented with different sleep times from .001 to 1 ([NSThread sleepForTimeIntervals .1] for instance). In spite of this the screen display is sluggish at best (10s of seconds) and pressing the stop button highlights the button but nothing happens again for 10s of seconds.
1.) Are NSOperation Queues a reasonable choice? If not, what else?
2.) How do I minimize the sleeping? (Obviously I want the work to get as many cycles as possible/reasonable, and I’m not sure that my sleeps are doing anything at all to all the UI to update.)
3.) Is there a better technique to keep the UI up to date? For instance, can I use NSTimer or some other method to send a message to the UI telling it to update and/or check the status of the buttons?
Thank you for your support.
1.) Are NSOperation Queues a reasonable choice? If not, what else?
NSOperationQueue sounds like it would be reasonable.
of course, you have choice: pthreads, libdispatch (aka GCD), c++ thread libraries built on top of pthreads, etc, etc , etc. if you don't spawn much/many, then it just comes down to the model you favor.
2.) How do I minimize the sleeping? (Obviously I want the work to get as many cycles as possible/reasonable, and I’m not sure that my sleeps are doing anything at all to all the UI to update.)
don't sleep =) you can use a timer for your ui elements or an explicit callback or notification to notify dependencies. if the dependencies peform ui updates, then you will likely add the message to the main thread's message queue.
3.) Is there a better technique to keep the UI up to date? For instance, can I use NSTimer or some other method to send a message to the UI telling it to update and/or check the status of the buttons?
that really depends on what you are doing. if you merely want to update a progress bar, then you can write the value from the secondary thread and read the value from the main thread. then use a timer on the main run loop to periodically message your object to update its display (based on the current value). for something like an unstaged progress indicator this may be good.
another alternative is more useful for events or stages: it would involve posting updates (e.g. notifications or callbacks to a delegate) from the secondary thread as progress is made (more info under #2).
Update
I wasn't sure this was appropriate in the iOS model, but it sounds like it is.
yes, that's fine - there are many appraches you can take. which is 'best' depends on the context.
My current understanding is to launch the UI in one thread (not the main!),
you really don't explicitly launch the UI; the main thread is (generally) driven by pushing events and messages onto the main thread. the main thread uses a run loop and processes the queued messages/events at each iteration of the run loop. you can also schedule these messages in the future (more on that in a bit). having said that, all your messages to UIKit and AppKit (if you target osx) objects should be on the main thread (as a generalization which you will eventually learn there are exceptions to this). if you have a specific implementation which is completely separated from messaging UIKit objects' methods and that program is thread safe, then you can actually perform those messages from any thread because it does not affect the state of the UIKit implementation. simplest example:
#interface MONView : UIView
#end
#implementation MONView
// ...
- (NSString *)iconImageName { return #"tortoise.png"; } // pure and threadsafe
#end
launch my worker thread, use a timer to generate a signal to the UI to take a look at a progress value and update the progress bar appropriately. For the purposes of this particular application your second to last paragraph is ample and I don't need to go to the lengths of the last paragraph (at least for now). Thank you.
to do this, you can use an approach similar to this:
#interface MONView : UIView
{
NSTimer * timer;
MONAsyncWorker * worker; // << this would be your NSOperation subclass, if you use NSOperation.
}
#end
#implementation MONView
// callback for the operation 'worker' when it completes or is cancelled.
- (void)workerWillExit
{
assert([NSThread isMainThread]); // call on main
// end recurring updates
[self.timer invalidate];
self.timer = nil;
// grab what we need from the worker
self.worker = nil;
// update ui
}
// timer callback
- (void)timerUpdateCallback
{
assert([NSThread isMainThread]); // call on main
assert(self.worker);
double progress = self.worker.progress;
[self updateProgressBar:progress];
}
// controller entry to initiate an operation
- (void)beginDownload:(NSURL *)url
{
assert([NSThread isMainThread]); // call on main
assert(nil == worker); // call only once in view's lifetime
// create worker
worker = [[MONAsyncWorker alloc] initWithURL:url];
[self.operationQueue addOperation:worker];
// configure timer
const NSTimeInterval displayUpdateFrequencyInSeconds = 0.200;
timer = [[NSTimer scheduledTimerWithTimeInterval:displayUpdateFrequencyInSeconds target:self selector:#selector(timerUpdateCallback) userInfo:nil repeats:YES] retain];
}
#end
note that this is a very primitive demonstration. it's also more common to put the timer, update handling, and operation in the view's controller, not the view.
Are you doing your UI updates on the main thread? This is very important because UIKit is not thread-safe and using it from a secondary thread can lead to sluggish behavior (or crashes for that matter). You usually should not need to use sleep in your background threads/queues for the UI to remain responsive (unless your UI itself is very CPU-intensive but that doesn't seem to be the case here).
You can check any of your methods that update the UI if they are running on the main thread with something like
NSAssert([NSThread isMainThread], #"UI update not running on main thread");
An easy and lightweight way to synchronize UI updates with the main thread is to use Grand Central Dispatch:
dispatch_async(dispatch_get_main_queue(), ^ {
//do your UI updates here...
});
Here you are my answers to your questions.
1) Since you are an experienced C programmer, you will feel comfortable with Grand Central Dispatch (GCD), a C based API for concurrency.
2) With GCD, you do not need to sleep at all. Simply dispatch asynchronously the work you need to do in a queue using the maximum priority (DISPATCH_QUEUE_PRIORITY_HIGH).
3) When you need to update the UI, simply dispatch on the main queue ( within the same block doing the work, using dispatch_get_main_queue() ) the UI update as needed.
Take a look at the relevant GCD documentation here.
I'd have a model object that does the CPU tasks, which has a delegate callback for when the output changes, and a view controller. In viewDidLoad you set the view controller as the delegate of your model. The model, therefore, can use threads and sends messages back on the main queue, when the calculated data has been updated. Unless your case is specifically complex, is just use Grand Central Dispatch and dispatch_async the intensive task onto another thread.
Certainly, you should not be calling sleepForTimeInterval anywhere to achieve what you want.
Related
I have a method, someMethod, that is called when a) the user taps the view and b) when a user drags the view. In someMethod, there is a UIView animateWithDuration block that makes the toolbar on top of the view disappear, and resets its frame accordingly. If the user taps the view, than drags it, someMethod will be fired while the animation is still completing, and this isn't the behavior I want (simply canceling the animation doesn't work because the completion block still fires (even if I check the 'finished' BOOL). All things being considered, I just don't want this method to be fired while the animation is still in progress.
Obviously an easy solution to this is to set a manual lock with a BOOL and only allow the method to be called once the lock is free.
I'm wondering, is there a more elegant way to accomplish this? Possible to use GCD or some other library to accomplish this so it's more fool proof?
Update: I did try to use synchronized, the problem though is the method fires off the animation, finishes, but the animation is still running on another thread. Any other ideas?
A timer running out does not imply or require a secondary thread. You're in control of what thread a timer is scheduled on. If you just schedule the timer on the main thread, then both things happen on the main thread.
The suggestions of using #synchronized achieve the effect that a given block of code is not running for the same object (whatever is the parameter of #synchronized) at the same time, but that's not the same thing as saying it's not run on two different threads.
If you want to detect if a method is called on a thread other than the main thread and then shunt it over to the main thread, you can use +[NSThread isMainThread] and dispatch_async(dispatch_get_main_queue(), ^{ /* re-call current method */ });.
In modern iOS and OS X, the most elegant mechanism for controlling the execution is to use dispatch queues and blocks. For a global lock, you can use a single serial queue and make request to it either synchronously or asynchronously, depending on whether you want the remainder of the execution on that thread to stop while you execute the critical code.
Declare your queue globally somewhere:
dispatch_queue_t myQueue;
So, when you launch, you'll create your queue:
myQueue = dispatch_queue_create( "CRITICAL_SECTION", DISPATCH_QUEUE_SERIAL); // FIFO
And when you want to execute the critical section of code, you use:
dispatch_sync( shpLockQueue, ^{
// critical section here
});
Depending on your needs, you might want to call your method within one of these blocks, or you might want to have the block within the object that you are protecting.
You could use the main dispatch queue for this, if you needed to make sure that the routine is run on the main thread, but if that's unnecessary, it's going to be more efficient to use your own queue. If you elect to use the main queue, you don't need to set up your own queue, or store it, you can just execute your code within:
dispatch_sync( dispatch_get_main_queue(), ^{
// critical section here
});
I would suggest the #synchronized() block, Heres a great blog post on the explanation of it:
http://googlemac.blogspot.com/2006/10/synchronized-swimming.html
#synchronized(self) {
[self someMethod];
}
Well even using just a global variable, doesn't guarantee mutual exclusion, since the variable is copied to the register before being updated, if that indeed is what you meant by "manual lock BOOL ..." and unfortunately their aren't any really elegant solutions ....
Check out https://developer.apple.com/library/mac/ipad/#documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html
Good luck.
I've been trying to implement this for a long time and I have gotten no favorable results.
Say I have a method in the which an HTTP request is performed (specifically, a twitter update), and say I want to display a UIActivityIndicatorView while the HTTP request is in progress (I know when it is done because there are delegate methods that are called when the request is done, either with positive results or negative ones).
I've seen many answers that say that threading is necessary for the implementation of this class. At first I tried calling the startAnimating method in a different thread and the stopAnimating method directly (without starting a new thread). After that I saw how this guy does it and I thought this was safer as I was starting and stopping the indicator in two different methods (the delegate methods for the twitter update).
However, none of this two ways of doing this have given me the results I want (the activity indicator does not show up at all). Is there anything I'm missing?
Thank you in advance and I apologize if my question is too long.
Your help is very much appreciated.
Threading is absolutely forbidden when working with UIKit subclasses. You may have seen reports that UIActivityIndicatorView uses threading internally, but in no way does that mean you can access the object from multiple threads. All UIView subclasses (including UIActivityIndicatorView) must only be accessed from the main thread. This includes calling -startAnimating and -stopAnimating.
If you rewrite your code such that you're only ever accessing the activity view on the main thread, and it still isn't working, then I would guess that the view was either not added to a visible view, is covered up by another view, or has a frame that puts itself outside of the visible area of its superview.
You cannot perform UI stuff in a secondary thread.
You should perform your HTTP request in a secondary thread, while calling the activity view from the main thread.
I recommend using DSActivityView which is so much easier to use. Just 1 line to show an activity view, 1 line to hide.
MBProgressHUD it's also easy and shows and hides itself when the secondary thread has started/finished. Something like this:
[HUD showhileexecuting:"yourstuff" animated:YES]
"Yourstuff" will run on a separate thread.
You should not perform UI activities in a secondary thread.
I am writing an app for the iPhone and arrived at this situation.
I have a view controller, myViewController, that will dealloc whenever the user taps the "back" button on the screen. There a thread in the background that communicates with a remote server and may message a method, updateUI, method in myViewController.
What would happen if the background thread messages updateUI in myViewController, but the user just happened to tapped the "back" button at the right time such that it causes myViewController to dealloc while updateUI is still executing?
My guess is that the dealloc method will run and the app might crash if updateUI ends up using a null pointer. Assuming this is the case, the current solution I have is:
[self retain];
// updateUI code here
[self release];
I am unsure if this is the best solution, as I feel that this is a common problem when dealing with multiple threads.
Is my assumption correct? If so, is there a better solution?
What you are describing is known as a "race condition." Race conditions can be difficult to identify in testing, track down once reported, and reproduce because sometimes execution in the debugger can effectively modify how the code is being executed (avoiding the condition that one is trying to reproduce). Race conditions are one of the major pitfalls in concurrent programming - making the area deceptively difficult to do well.
In principle, it is a best practice to minimize the use of shared resources and closely qualify how the sharing is coordinated when implementing concurrency. If an object is shared across multiple threads, it should be retained by each of them to ensure that the object stays in-scope while each thread completes its processing.
Apple has been taking steps to simplify implementing concurrency. This is a good starting point for familiarizing yourself with the topic on iOS.
http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html%23//apple_ref/doc/uid/TP40008091
It's also useful to be aware that Objective-C 2.0's properties can support atomic operations (and are atomic by default, thus the nonatomic keyword to disable this default).
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html
And, this is the old-school guide to threads (out of favor approach, but still useful background - be sure to be familiar with NSLock).
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/Introduction/Introduction.html%23//apple_ref/doc/uid/10000057i
Whenever some part of your code depends on another, it should retain this dependency until it is not needed. In your case the background worker should retain the controller and only release it when the work is done (or cancelled).
If you dealloc and then nil your objects then this shouldn't be an issue - you can send messages to nil in objective-c.
Alternatively, if you wanted the viewController to get the message and if your targeting iOs 4, you can use blocks and GCD. Blocks auto retain objects and therefore if a block references your viewController, would keep it around for as long as its needed, even if -(void)dealloc ; has been called.
Here is a decent Block tutorial
Yes, your app will crash, likely with something along the lines of EXC_BAD_ACCESS.
As far as multithreading, you will want to retain your objects until everything is done with them and program defensively. Check for the existence of objects before trying to manipulate them.
I am just trying to understand this: multithread is a way of firing a method in another thread, so it will not hang the main thread (when something heavy must be done). But I have seen stuff like this:
this goes on the main code:
[NSThread detachNewThreadSelector:#selector(createNewThread) toTarget:self withObject:nil];
and here goes the thread
-(void) createNewThread {
NSAutoreleasePool* p = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(doSomeStuff) withObject:nil waitUntilDone:YES];
[p release];
}
My problem with this is: if the thread is intended to relieve the main thread from doing something, why is doSomeStuff being called using performSelectorOnMainThread? Isn't that insane? I mean, you fire a second thread just to run something on the main thread, a thread that in fact you shouldn't be using.
I have seen a lot of code on the web write this way.
I know that probably it is done that way because it is doing something that is not thread safe, but is doing this really useful? To me sounds that it is totally useless and it would be better to do something like
[self performSelector:#selector(doSomeStuff) withObject:nil afterDelay:0.01];
what do you guys think?
thanks in advance.
performSelectorOnMainThread:withObject:waitUntilDone: puts the call on a queue to perform on the next iteration of the run loop. performSelector:withObject:afterDelay: sets up a timer to perform the action. However, creating a new thread to call performSelectorOnMainThread makes no sense because it puts the call on the queue even if it is already on the main thread.
If that's the entire body of the createNewThread method then it makes no sense. But if other stuff actually happens in createNewThread then there might be a good reason. The most common scenario for having a background thread use performSelectorOnMainThread: is if the UI needs to update as a result of the background activity. Like if you're processing a large data set and you want to show the user how much progress has been made so far. UIKit is not thread-safe so you can't update the UI from the background thread. But you might do something like, have the background doing a lot of work in a loop, and at every pass through the loop (or every 5, or whatever) call back to the main thread to update the UI. This makes the code switch back to the main loop just long enough to update the UI before returning to the background for the heavy duty work.
I wrote some sample code in a separate application, that basically queues up items in an NSMutableArray. I then have a method which loops through that array and creates an NSInvocationOperation, puts it in the NSOperationQueue, releases the operation and so forth. The method then gets called (simply prints out the string that was stored in the array and passed into the operation), does it's job, and sends an NSNotification back to the ViewController. The observer method gets hit, but the operations have a lag on them. For instance, the observer method simply updates a UILabel with how many messages are left in the queue. It eventually does this, but there seems to be a five second lag in between all of the NSOperations completing and the UI updating. To me it seems like the NSOperationQueue is blocking the main thread. Is there anyway to get the UI to respond immediately to the notifications?
One important note is that I have not tested this on the phone yet, just the simulator. I'm not sure if this makes a difference.
Sorry in advance. I'm away from my computer and I don't have the code in front of me. Hopefully I explained it well enough. Also I have read the documentation, just haven't found anything that's really answering this specific question for me.
The delay is typical of UI updates that are performed on threads other than main.
To see your update instantly on the UILabel, be sure to invoke whatever method is updating the label's text as follows:
[self performSelectorOnMainThread:#(myMethodToUpdateLabelWithText:) withObject:text waitUntilDone:NO];
where myMethodToUpdateLabelWithText is a method within your class that sets the label's text value.