> Problem is - though I am able to perform an asynchronous task using
> these classes, I am unable to perform a repetitive task (as in my
> case) using these classes.
>
> Also I know that I can use NSTimer but I wanted to know if same
> functionality can be achieved through NSOperationQueue and
> NSDispatchQueue and if yes then how?
>
> Can someone throw some light on this and guide me towards the correct
> direction?
I'd recommend dropping down one more level and using GCD. It's a little uglier than NSOperationQueue, but really not much. The API is pretty usable, and *much* more powerful.
For your particular use, a dispatch timer will do what you need. You can create one using dispatch_source_create and the DISPATCH_SOURCE_TYPE_TIMER type. Target it at one of the GCD global queues and you have a periodic task that runs in a background thread without much work on your part.
For more information on dispatch timers, I have a bit of discussion on them near the end of this article:
Mike
On 2012-01-05, at 10:30 AM, Decoder wrote:
> -(void)refreshUserIdPassword
> {
> [self getAllUserIdsPasswordsContinousely];
> [NSThread sleepForTimeInterval:180];
> [self refreshUserIdPassword];
> }
Aside from mis-spelling 'continuously' (sorry, can't help it), this is calling itself every three minutes, so the stack will keep growing infinitely. It'll take a long time, sure, but it's still a Bad Thing.
> I have read that NSThread is not the best way to perform background
> task, and there are other classes provided in cocoa, such as -
> NSOperationQueue and NSDispatchQueue, which should be preferred over
> NSThread to perform an asynchronous task. So I am trying to implement
> the above specified functionality using the alternative classes.
I believe you mean dispatch_queue_t? There's no such class as NSDispatchQueue that I can find, and NSOperationQueue is already built on top of dispatch queues, which are a C-level construct.
> Problem is - though I am able to perform an asynchronous task using
> these classes, I am unable to perform a repetitive task (as in my
> case) using these classes.
Not entirely on their own, no— they're designed for enqueueing discrete jobs, not for performing something on a schedule.
> Also I know that I can use NSTimer but I wanted to know if same
> functionality can be achieved through NSOperationQueue and
> NSDispatchQueue and if yes then how?
Yes, you should be using NSTimer, like this:
...
// run the method right now for the first time...
[self getAllUserIdsPasswordsInBackground];
// ...then set up a timer. It will fire for the first time in 180 seconds (three minutes), and every three minutes thereafter
self.passwordTimer = [NSTimer scheduledTimerWithTimeInterval: 180.0 target: self selector: @selector(getAllUserIdsPasswordsInBackground) userInfo: nil repeats: YES];
...
- (void) getAllUserIdsPasswordsInBackground
{
[self performSelectorInBackground: @selector(getAllUserIdsPasswordsContinuously)];
}
When you're done with the timer, do:
[self.passwordTimer invalidate];
self.passwordTimer = nil; // assuming you're using GC or ARC. Otherwise you need to -release the timer too.
You can also use a dispatch timer source to have everything run in the background automatically, but this is a bit more involved than the above:
// create the timer:
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
// setup an event handler which will run on the target queue (global default-priority queue was specified at creation above).
dispatch_source_set_event_handler(timer, ^{
[self getAllUserIdsPasswordsContinuously];
});
// dispatch sources are created in a 'suspended' state, so you must 'resume' them to start them off
dispatch_resume(timer);
That looks simpler though, no? Why did I say it was involved?
Because the block password to dispatch_source_event_handler() is using 'self', which causes 'self' to be automatically retained along with the block. This means that, unless you manually dispatch_release() the timer, your object's -dealloc method will never be called. Therefore, you can't rely on being able to release the timer in your object's -dealloc method. The same is NOT true of NSTimers-- they don't retain their targets, thus you MUST -invalidate any NSTimers in your -dealloc method.
Now, if you're using ARC or garbage collection, you needn't worry about keeping track of the NSTimer-- if it's a member variable (or a property) then the system will arrange for it to be released at the appropriate time. However, the same is NOT true of the dispatch timer-- dispatch objects are not Objective-C objects, so you must still implement -dealloc under ARC (or -finalize under GC) in order to release it, which still results in a retain loop. Dispatch objects are neither automatically reference-counted nor garbage-collected, although *blocks* are, which is from where the problem ultimately arises.
However: you CAN use the dispatch timer approach so long as the block doesn't reference 'self' at all. If the -getAllUserIdsPasswordsContinuously function simply calls some external function, then you can just call that external function from your timer's event-handler block and you'll be fine, i.e.
dispatch_source_set_event_handler(timer, ^{
// doesn't reference 'self', and so does not cause a retain cycle.
[[MyPasswordHandler sharedInstance] updateUserIdsAndPasswordsNow];
});
My advice: use an NSTimer. Unless it's ABSOLUTELY ESSENTIAL that this method be called EXACTLY every 180 seconds on a predefined schedule (NSTimer will *count* 180 seconds of live time after each fire invocation is complete), that's your simplest and best option. If you really need a real-time timer, then you likely want something significantly more complicated again. So just stick with NSTimer.
This is off-list because I don't want to add to the noise and it's not
important.
On Fri, Jan 6, 2012 at 3:00 AM, Jim Dovey <jimd...@gmail.com> wrote:
> Aside from mis-spelling 'continuously' (sorry, can't help it), this is calling itself every three minutes, so the stack will keep growing infinitely. It'll take a long time, sure, but it's still a Bad Thing.
Actually, I doubt it will. I believe the compiler will use tail call
recursion. Still, I wouldn't recommend relying on it, not least
because other developers looking at the code will think (like you did)
that there's a problem with it.
Kind regards,
Chris
> This is off-list because I don't want to add to the noise and it's not
> important.
Ha! Sorry. I meant to send it off-list.
Kind regards,
Chris