1

I want to use dispatch_async to occasionally pull a changing resource from my web server (about every 60 seconds).

I want to make use of dispatch_async but I want to give it a function name instead. Since I want that function to sleep and redo the same thing again.

This is what I am trying to do dispatch_async(self.downloadQueue, @selector(getDataThread:)); (this does not compile)

I think a while loop is not a good idea so I'm wondering what was the best approach for this

1
  • 2
    If you can refactor it into a C-function you can use dispatch_async_f() Commented May 28, 2013 at 2:20

2 Answers 2

4

A couple of observations:

  1. If you want to create a timer for a dispatched operation, create a dispatch_source_t of type DISPATCH_SOURCE_TYPE_TIMER, e.g., define two class properties:

    @property (nonatomic, strong) dispatch_queue_t  queue;
    @property (nonatomic, strong) dispatch_source_t timer;
    

    And then create the timer:

    - (void)createTimer
    {
        self.queue = dispatch_queue_create("com.stackoverflow.16782529", 0);
        self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue);
        if (self.timer)
        {
            dispatch_source_set_timer(self.timer,
                                      dispatch_walltime(NULL, 0),
                                      60ull * NSEC_PER_SEC,
                                      1ull * NSEC_PER_SEC);
    
            dispatch_source_set_event_handler(self.timer, ^{
                NSLog(@"doing something");
            });
    
            dispatch_resume(self.timer);
        }
    }
    

    By the way, like all repeating timers, it's probably good practice to create in your viewDidAppear and remove in your viewDidDisappear:

    - (void)viewDidAppear:(BOOL)animated
    {
        [self createTimer];
    }
    
    - (void)viewDidDisappear:(BOOL)animated
    {
        dispatch_source_cancel(self.timer);
    }
    
  2. If you don't want to use blocks, use the _f equivalent of any of the GCD functions. All of the GCD functions that take a block have a rendition (suffixed with _f) that take a C function instead. For example, using the above example, the non-block rendition would look like:

    - (void)createTimer
    {
        self.queue = dispatch_queue_create("com.stackoverflow.16782529", 0);
        self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue);
        if (self.timer)
        {
            dispatch_source_set_timer(self.timer,
                                      dispatch_walltime(NULL, 0),
                                      60ull * NSEC_PER_SEC,
                                      1ull * NSEC_PER_SEC);
    
            dispatch_source_set_event_handler_f(self.timer, &myTimerHandler);
    
            dispatch_resume(self.timer);
        }
    }
    
    void myTimerHandler(void *context)
    {
         NSLog(@"doing something");
    }
    
  3. If you'd rather use a NSTimer (which uses an Objective-C method rather than a C function), you might have properties like:

    @property (nonatomic, strong) dispatch_queue_t  queue;
    @property (nonatomic, strong) NSTimer *timer;
    

    And then create the timer when the view appears and remove it when it disappears:

    - (void)viewDidAppear:(BOOL)animated
    {
        self.queue = dispatch_queue_create("com.stackoverflow.16782529", 0);
        self.timer = [NSTimer scheduledTimerWithTimeInterval:60.0 target:self selector:@selector(handleTimer:) userInfo:nil repeats:YES];
    }
    
    - (void)viewDidDisappear:(BOOL)animated
    {
        [self.timer invalidate];
    }
    
    - (void)handleTimer:(NSTimer *)timer
    {
        dispatch_async(self.queue, ^{
            NSLog(@"doing something");
        });
    }
    
  4. Personally, if you're checking this frequently, then maybe a pull architecture (where you're constantly pulling data down every 60 seconds) might not make sense. You could consider either opening a socket to the server and have the server notify you when there is a data update (see this Ray Wenderlich example), or employ push notifications (the notifications documents talk about both local notifications and push notifications, but for this application, you'd be looking at push notifications). Clearly these both require some more server development, but it's architecturally more elegant/efficient.

  5. By the way, if you are going to trigger your network event every 60 seconds, if you're initiating your network activity asynchronously (either via the NSTimer method of #3, or if your network request, itself, runs asynchronously) you might also want to introduce some check to make sure that the previous request has completed before initiating another. It's extremely unlikely that it wouldn't have, but you should check this nonetheless. If you use the dispatch_source_create method and use a synchronous network request, this isn't an issue (because a timer won't be retriggered until the prior one finished), but otherwise, you might want to be careful you don't end up backing up your queue with network requests.

Sign up to request clarification or add additional context in comments.

7 Comments

A dispatch_source_t cannot be strong or retain in Xcode 7.1.1. Your code will not compile.
@Cœur - No, that's not true. If you're seeing that error, it's probably because you set compiler flag -DOS_OBJECT_USE_OBJC=0, which as described in <os/object.h>, turns off object behavior for GCD types. But I retested this in Xcode 7.1.1 (as well as Xcode 7.2 beta 3), and strong works fine (and, in fact, is needed).
I do not have the flag OS_OBJECT_USE_OBJC anywhere in my project
@Cœur - You'll also get this error if you target OS X versions prior to 10.8 or iOS versions prior to 6, as that predates the object handling of GCD types, and thus requires manual dispatch_retain and dispatch_release.
True, I'm targeting iOS 5.1.1. Any idea on how to solve the compilation error?
|
0

How about something like...

NSTimer *timer = [NSTimer timerWithTimeInterval:60 target:self selector:@selector(getDataThread:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

- (void)getDataThread:(id)foo {
    dispatch_async(self.downloadQueue, ^{
       // code you want to run asynchronously
    });
}

It seems you want some block of code to run asynchronously... but you want that to also run at a timed interval, so I think this should suffice.

If you want to ditch blocks and dispatch_async altogether you could look at NSOperationQueue, keeping in mind NSOperationQueue is built on GCD but you don't need to worry about interfacing with it directly.

6 Comments

awesome, i think that should work. I want this to occur as long as the app is open. Can I put that in didFinishLaunchingWithOptions?
Sure I think that should be fine, though I'd wrap it in a method and also put it in applicationWillEnterForeground: and then stop the timer under applicationDidEnterBackground:.
and how do you stop the timer?
@jamesatha To cancel the timer, keep the NSTimer in a class variable, and then call [timer invalidate];. This is very important, or else the repeating timer can cause a retain cycle. (And don't try to do this in dealloc, but rather something like viewDidDisappear of whatever makes sense for your business logic.)
I'm not sure why you're creating the timer and adding it to the run loop like that. Why not just timer = [NSTimer scheduledTimerWithTimeInterval:60.0 target:self selector:@selector(getDataMethod) userInfo:nil repeats:YES]; which creates the timer and schedules it for you?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.