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.
Related
I'm trying to understand how things work in regards to concurrent programming and calling setNeedsDisplay. I basically have Three objects.
Main View - container with different UIView objects, the main one being a UIScrollView
Small Map View - a small UIView that draws a miniature version of one of the other UIView items on screem
Processor - a delegate of the Main View that calculates what's on screen and calls the Main View back with what's in view.
So a simple use case of what's going on is the user touches the ScrollView and then the processor updates what's in view of the scrollView (like calculating coordinates, center point, etc) It does this using blocks and does it asynchronously. This then posts a notification to the MainView object.
When the MainView receives the notification, it just calls
[smallMap setNeedsDisplay]; // example 1
I put some logs around this call, and I do see it gets called right away. However, the drawRect: of this function does not get called right away. It gets called after 2 seconds or so.
I remember reading that setNeedsDisplay just marks the view for redraw to happen on the next event of the run loop.
But if I add this code instead:
// example 2
dispatch_async(dispatch_get_main_queue(), ^{
[smallMap setNeedsDisplay];
});
My view gets redrawn right away.
I guess I'm confused as to why I have to ask for the main event loop to call setNeedsDisplay to immediately redraw something. Like in example 1, by me calling setNeedsDisplay, is that done in the background or something and that's why it doesn't get redrawn right away? I'm trying to understand the difference in what's going on behind the scenes so I know what to look for the in future. Like should I have all my calls that need to be immediately redrawn in something similar to the example 2 block? Or is it because I'm processing my data asynchronously that I need to then ask for the main queue? Thanks!
My guess is 1 of 2 things:
Your code that is running on a separate thread is calling your MainView methods from the separate thread instead of using performSelectorOnMainThread or a GCD call that invokes the code on the main thread. Thus your call to setNeedsDisplay is actually taking place on a background thread, which is a no-no, as the other poster said.
The second possibility is that your MainView code is running on the main thread, but it gets busy doing time-consuming processing, or waiting for a synchronous call to another thread to finish, and doesn't service the event loop.
You can rule out the first possibility by setting a breakpoint on your call to setNeedsDisplay and looking at the call trace in the debugger to see what thread it's running from.
Figuring out the second possibility will take a little more work. You might need to delve into instruments.
setNeedsDisplay is a UIKIT API call and has to be called from the main thread of the application, also known as the UI thread. That's why calling it in a background thread doesn't have any immediate effect and scheduling it on the main queue has immediate effects.
See this related question https://stackoverflow.com/a/6988115/172690 for a more detailed answer.
When I cancel an NSOperation (when user presses a button) cancel method is called from the main thread, but evidently the operation is running in another thread.
So, to avoid race conditions when I change _isExecuting and _isFinished, I think cancel (or at least its logic) should be called from the same thread that the NSOperation. Apart from that, when user cancels it, several files are deleted and it takes time. Because cancel is called from main thread, all the app becomes unresponsive for a while, which is ugly.
How can I execute cancel code in the same thread that the current NSOperation?
I tried this in cancel (similar to what I saw in ASIHTTPRequest):
if (_operationThread) {
[self performSelector:#selector(cancelOnRequestThread) onThread:_operationThread withObject:nil waitUntilDone:NO];
} else {
[self cancelOnRequestThread];
}
And _operationThread is setted in start method using:
_operationThread=[NSThread currentThread];
But it doesn't work.
Any idea or suggestion?
Note: I use concurrent operations, so I use start instead of main.
Thanks a lot for help.
Ricardo.
It's fine to call cancel on an NSOperation from the main thread. The cancel method is thread-safe.
That shouldn't cause any blocking on your main thread because the cancel method itself shouldn't be doing any work. If you have overridden the cancel method of your operation to delete files, etc then that is the wrong approach. You shouldn't override the cancel method, instead just check the isCancelled method at regular points within the operation's main method (e.g. inside any tight loops) and then return from main early if isCancelled returns YES, which will then cancel the operation on the same thread as the rest of the execution.
If that's how you've implemented it already and you're still having performance issues, is it possible that your operation is not really running on a background thread at all? For example if you've added it to the queue returned by [NSOperationQueue mainQueue] then that's actually running on the main application thread.
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.
I need to put first image on screen, then put the second after 1 second, and then - the last one after one more second. Also, this should be done in non-blocking manner :) How do you perform that in such cases? Was thinking about nesting several UIView animateWithDuration class methods, but I do not need any animations, I just need to put several images on screen in straight manner with a delay after each step.
UPDATE: IMHO it's too heavyweight approach to use NSTimer for this simple problem. I should need to get a reference, schedule 3 different timers (or one with very if'y method) and then to stop somehow. If so, I would like to to it all from within one method. In that case, if I initiate timer with scheduledTimerWithTimeInterval then IMHO I need to stop (invalidate) somewhere else or smth?
Use NSObject's performSelector:withObject:afterDelay:.
use [NSThread detachNewThread:Selector:toTarget:withObject:]. You can get the event when the current thread is finished by using [[NSThread currentThread] isFinished]. Thats the way you can do all task with a non-blocking manner. For more information see the NSThread Class reference here
Happy Coding!
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.