0

BTW: This mostly is in reference to iOS 6+:

I have an app that -- from many different places in the app -- performs a function that can be backgrounded safely, and is thread safe. I do not wish to stop the system while this function takes place, so instead of calling it as: [self functionName]; currently I fire off a NSTimer with a short short interval:

 [NSTimer scheduledTimerWithTimeInterval:.5 target:self selector:@selector(doThis:) userInfo:@"someString" repeats:NO];

My question is this: I have begun to investigate NSNotification for this simply because I hate the idea of timers... they seem queued and do not let the system decide (in my opinion) which order to do them in. FIFO with most of them...or -- indeed -- all at the same time. You never know. So, in comes NSNotification:

[[NSNotificationCenter defaultCenter] postNotificationOnMainThreadName:@"doThis" object:nil userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"a",@"varA",@"b",@"varB",nil]];

Now, which is better for the system...resource-wise and, really, all around? NSTimer or NSNotification?

Obviously the nice thing of NSNotification is that once defined in the appDelegate -- can be called from any aspect of the app. NSTimer would be able to do that but it would require the requisite "target"...a few extra lines of looking up the target, etc.

Suggestions?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Jann
  • 2,214
  • 3
  • 28
  • 45

2 Answers2

2

Neither timers nor notifications is appropriate for this. Just run the method in the background. You have a few options:

[self performSelectorInBackground:@selector(doThis:) withObject:@"someString"];

The background thread needs to be setup properly with an autorelease pool. Assuming you are using ARC you do this by ensuring your doThis: method looks like this:

- (void)doThis:(NSString *)someParam {
    @autoreleasepool {
        // rest of the code here
    }
}

Better yet, use GCD (Grand Central Dispatch) instead of using performSelectorInBackground:withObject::

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self doThis:@"someString:];
});

This works fine if doThis: does not do anything that updates the user interface. If doThis: does need to update the user interface at some point, then the interface code must be done on the main thread. That code should be wrapped in:

dispatch_async(dispatch_get_main_queue(), ^{
    // perform UI update here
});
rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • @maddy: Thanks, however, wondering why the overhead involved in setting up and tearing down a dispatch_async call is less than NSNotificationCenter. I thought that when you had a function that was to be called when certain things occurred, yet the function did not need to know anything about the 'thing' that occurred, NSNotification was the way to go. For instance (simplistically), a update to a counter whenever a certain view was shown. Then put the NSNotification at the viewWillAppear and it fires off a notification. The "listening" function then updates the page view counter. No slowdown. – Jann May 10 '13 at 17:35
  • Perhaps I misunderstood your needs. You use NSNotification when you have one bit of code that wants to make an announcement and that code doesn't care who (or how many) is listening and what they do with the notification. The code I posted describes how to execute code in the background. From your comment to my answer, one class should post a notification that indicates "x happened". Then some other class registers for the notification and when the notification is received, it can do whatever it needs, including running some method (on main thread or the background). – rmaddy May 10 '13 at 17:49
0

NSTimer is just wrong, and always was. The timer has nothing whatever to do with what's going on: you're just guessing that .5 seconds from now everything will be over and we can proceed. Do not program by guesswork. If we need to know when a certain method finishes, then respond to the method finishing.

matt
  • 515,959
  • 87
  • 875
  • 1,141