I cant seem to find a suitable answer on SO or google.
I am hitting a web server for new data every 15 seconds. The problem is while waiting for a response from the server (a few seconds at times) my user interface is tied up. I cant seem to figure out how to start an NSTimer on a seperate thread so I can pull new data in the background, leaving my UI buttons responsive.
Any suggestions?
Thanks!
When you request from the server, you can use an asynchronous NSURLRequest to receive the data. Then, you could keep your NSTimer in the main thread, because all it would be doing is creating an asynchronous NSURLRequest every 15 seconds.
Otherwise you can create a NSTimer in the background thread by creating the timer with
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
Then add the timer to whatever run loop you want by calling
[[NSRunLoop currentRunLoop] addTimer:MY_TIMER forMode:NSDefaultRunLoopMode];
(Replace currentRunLoop with the run loop that you want the NSTimer to be in.)
There are various ways of working with threads, but GCD (Grand Central Dispatch) is probably the easiest.
Here is a tutorial example building a simple job queue in GCD.
Related
I have been tasked to write an app that allows a user to schedule emails to be sent out in future.
The user selects a date time from a date picker, composes the message and recipient and then schedules the event. When the date/time occurs the message is sent out.
Can someone guide me to how to get about scheduling lets say a text message. I know how to send a text message. Just was not sure on the scheduling aspect of things.
Any pointers will be much appreciated.
The first response will technically allow you to establish a timer that will fire every 2.5 seconds, however the original poster asked for a solution that would fire at a specific time. For that you need to use the following method of NSTimer:
- (id)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
The first argument is an NSDate indicating when the timer should fire.
The original poster did not specify, but if this is an iOS app then it is important to understand that timers scheduled to fire at a distant date/time will not fire if your app is not the foreground app. In fact there is no way to schedule such an event to occur when your app is in the background on iOS, so you must take that into account.
Here's a snippet of code which sets a one use timer to call self's imageSavedLabelOff: selector with itself (the timer) as the object parameter to the method. The timer schedules the call to be made in 2.5 seconds.
NSTimer *quickie = [NSTimer scheduledTimerWithTimeInterval:2.5 target:self selector:#selector(imageSavedLabelOff:) userInfo:nil repeats:NO];
You may have already found the answer by now but for future visiters like me I would like to suggest an answer- i.e. EventKit :
https://developer.apple.com/library/ios/documentation/DataManagement/Conceptual/EventKitProgGuide/ReadingAndWritingEvents.html
You can schedule/fetch events for any time and do your stuff accordingly. Hope this helps somebody.
You should be able to achieve this using NSRunLoop. Check out the Threading Programming Guide.
Apart from the use of NSTimer, you should be aware that sending of the E-Mail can fail for several reasons (no network available and others). Then you need to reschedule the request, maybe give up after 3 retries and notify the user about this.
You can use -
[self performSelector:#selector(myFunc:) withObject:nil afterDelay:5.0];
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 iPhone app, I have to perform function constantly in background.
For that I think I will have to use NSThread to call the function and keep it executing in background.
I dont want to stall my app and hence I want to use NSThread to keep my Main Thread free for user interaction.
How should I implement NSThread to perform the function in background?
EDIT:
The function is for fetching the data from a web server every 20 seconds and updating the tables in my iPhone app based on the data that is fetched from the web server.
I'd look at an NSOperationQueue first.
I'm guessing that your background task is really a small task repeated again and again. Make this into an NSOperation subclass and just add them onto an NSOperationQueue. That way you can control the background tasks more easily.
You also get the advantage with an NSOperationQueue that when there are no operations to run, the processor isn't just stuck in a while(YES) loop, waiting. This will help your app's UI be more responsive and will help battery life :)
However, if your background task is a single long running task that just needs to be started and then ignored, performSelectorInBackground isn't too bad an idea.
Sounds like a bad idea, but it's very simple.
[self performSelectorInBackground:#selector(theMethod:) withObject:nil];
Just have a while(YES) in theMethod: and it will never stop executing.
EDIT:
Luckily for you it's just as simple to do something once every 20 seconds.
[NSTimer scheduledTimerWithTimeInterval:20 target:self selector:#selector(theMethod:) userInfo:nil repeats:YES];
This will execute theMethod: once every 20 seconds. I might also add that this is a much better idea.
you'll want to interact with the thread's run loop. if it's an NSThread, it is created automatically. thus, you are given access to the CF/NS-RunLoop - typically by calling + [NSThread currentRunLoop] from the secondary thread.
in that case, you add a CF/NS-Timer to the run loop, and let it run and repeat until your work is finished. when the timer fires, your thread is awoken, and you then do your work.
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.
For a game I'm developing, I call an expensive method from one of the touch processing routines. In order to make it faster, I decided to use performSelectorInBackgroundThread, so instead of:
[gameModel processPendingNotifications];
I switched to:
[gameModel performSelectorInBackground:#selector(processPendingNotifications) withObject:nil];
The first problem I had, is that processPendingNotifications did not have a NSRunLoop, so I added it, like this:
- (void)processPendingNotifications {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pendingNotificationsQueue makeObjectsPerformSelector:#selector(main)];
[pendingNotificationsQueue removeAllObjects];
[pool drain];
}
So far so good. My problem is that some of the methods that are called from this background thread create new NSTimer instances. These instances, end up not firing. I think this is because the secondary thread I have doesn't have a (or is ending its) NSRunLoop. I'm starting the new timers by using:
[NSTimer scheduledTimerWithTimeInterval:20.0 target:self selector:#selector(timerFired) userInfo:nil repeats:NO];
My questions are:
Am I on the right path of suspecting the problem has to do with the NSRunLoop ?
Is there a way I can start a NSTimer from a background thread and attach it to the main thread's NSRunLoop ?
Yes, you need a runloop to dispatch the timer events
NSTimers are implicitly attached to the runloop of the thread they are created on, so if you want it to be attached to the main thread's runloop create it on the main thread using performSelectorOnMainThread:withObject:waitUntilDone:. Of course that code will execute on the main thread.
If your question is "Can I have a timer run on the main thread's run loop that directly runs a selector on another thread?" The answer is no, but the selector it fires on the main thread could just performSelector:onThread:withObject:waitUntilDone:. Of course that requires the thread you are trying to perform the selector against have an operational runloop.
If you want the code the timer is triggerring running on a background thread then you really need to get the background thread's runloop going, and if you do that then you don't need to schedule anything with the main thread since you will have an operational runloop.
The NSTimers actually just periodically fire events into the enclosing NSRunLoop, which each thread has (or should have). So, if you have a child (or background) process running in a different thread, the NSTimers will fire against that thread's NSRunLoop instead of the application's main NSRunLoop.
You could ensure that timers are always created against the main runloop by sending it the addTimer:forMode: message with your newly instantiated (but not started) NSTimer. Accessing the main application's run loop is done with [NSRunLoop mainRunLoop], so regardless of which thread you're in, doing [[NSRunLoop mainRunLoop] addTimer:[NSTimer timerWithTimeInterval:20.0 target:self selector:#selector(timerFired) userInfo:nil repeats:NO]] forMode:NSDefaultRunLoopMode] will always schedule the timer against the main runloop.
However, bear in mind that the execution is not guaranteed at that interval, and if your main loop is busy doing something your timer will be left waiting until it's ready.
It's worth considering NSOperation and NSOperationQueue if you really want background activity to occur.