Question

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

Was it helpful?

Solution 2

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top