I am trying to get my UIActivityIndicator start animating when the process of data consumption start downloading. My concern is: how do you get the activity indicator start animating at the same time? I have gone through some solutions here on Stack Overflow, yet I am trying to find some universal solution, since I am utilizing a subclass of UIActivityIndicator. I am not sure if a separate thread can resolve this issue?
I used:
[self performSelector:#selector(methodname) withObject:nil afterDelay:0];
However, this didn't solve my problem.
Just return from the method starting the activity indicator. The UI (including the activity indicator) updates after an app returns to the main run loop. Handle any long activities (such as your download) asynchronously with delegates or callbacks, so that the activity indicator can continue to run. So return from the method that starts your download as well. Finish later in another method.
Another option is to do the download, or other long process, in another thread. Operation queues and blocks can be used for this purpose. Use a performselector on the main thread to update the UI, as an app can't update the UI directly from within a background thread.
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'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.
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.
In my application i am uploading some images to the servelet, which takes some time to complete upload. I have implemented an uiactivityviewer using IB and connected it to an outlet named acticityIndicator. But when i call
[activityIndicator startsAnimating];
nothing is happening!!!
What might be the problem.
i've debugged the code,
the statement is executing, but nothing happens!!!
Are you performing your uploading on the main thread, or kicking off a background thread to do it?
If the former then you are blocking the UI thread so it doesn't have a chance to update the activity indicator.
I am working on a an application which is very simple
a navigation controller with a table view
when the user clicks a row, he is directed to the details view.
However, the details view pulls data from Core Data. i am pulling a relatively large amount of data that takes about three seconds to load.
I wanted to add that UIActivityIndicatorView to show progress.
I tried to start the animation once the user clicks the row, so i set it to animate in didSelectRowAtIndexPath
For some reason, the Activity Indicator doesn't start before the pushing of the details view.
Any idea why? or the best way to implement such an idea?
~Adham
Because you start the animation and then start a large operation in the same thread. Consider running that 3 second operation in a new thread. Look at NSOperationQueue and then create a NSOperation to run that procedure. It will work this way.
The UI doesn't update until the end of your run loop. You are, in sequence, displaying the activity monitor, then pushing the new table view, and then the UI updates. You need to change this order.
You can either move something to a different thread, or you could perhaps delay the loading of the new table view by calling performSelector:afterDelay: with a delay of 0. That will delay the loading of the new table view until after the activity indicator appears in the UI. Now, it's still all on the same thread, so you will be blocked from doing anything, but if the animation is threaded in the activity monitor, it would make for a quick and easy solution.
Call method in thread:
[NSThread detachNewThreadSelector: #selector(loadMethod) toTarget:self withObject:nil];
See following for more details:
http://iphone.zcentric.com/?s=UIActivityIndicatorView