3

How to preform certain operation in background on different thread, if it is executed on main thread it is blocking the UI of my app. any one have any idea how to do it?

even if it prints NSLog in background it will be fine. i want to run following thing in no matter even if user presses HOME button. in my viewController i did this :

- (IBAction)btnStartClicked:(UIButton *)sender {
    [NSThread detachNewThreadSelector:@selector(StartBGTask) toTarget:self withObject:nil];
     }

-(void)StartBGTask{
    [[[UIApplication sharedApplication] delegate] performSelector:@selector(startThread)];  
  }

and in appDelegate.m i have this method

 -(void) startThread {
 @autoreleasepool {
    for (int i = 0; i < 100; i++) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"current progress %d", i);
        });
        [NSThread sleepForTimeInterval:1];
    }
   }
 }

it prints integer from 1 to 100 on interval of 1 second.

Rishabh Srivastava
  • 3,683
  • 2
  • 30
  • 58
Ravi_Parmar
  • 12,319
  • 3
  • 25
  • 36
  • Your for () {} is performing in main queue. And you use [NSThread sleepForTimeInterval:1], isn't it blocks your UI? You stops you main queue. – Timur Bernikovich Jul 31 '13 at 09:59
  • Edited and included more details, i tried to perform execution on second method on different thread but it stops background task when home button is pressed. – Ravi_Parmar Jul 31 '13 at 10:11
  • Why not just use a timer. You don't even need to put that on a background thread. – Abizern Jul 31 '13 at 12:02
  • As I said below, I think his point it's not print number 1 to 100, this was maybe just some "helloWorld for threads" to see if it was working. – Lucas Eduardo Jul 31 '13 at 12:59

3 Answers3

8

Add these properties to your .h file

@property (nonatomic, strong) NSTimer *updateTimer;
@property (nonatomic) UIBackgroundTaskIdentifier backgroundTask;

Now replace btnStartClicked method with this,

-(IBAction)btnStartClicked:(UIButton *)sender {
    self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
                                                        target:self
                                                      selector:@selector(calculateNextNumber)
                                                      userInfo:nil
                                                       repeats:YES];
    self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        NSLog(@"Background handler called. Not running background tasks anymore.");
        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
        self.backgroundTask = UIBackgroundTaskInvalid;
    }];
    
}

 -(void)calculateNextNumber{
    @autoreleasepool {
      // this will be executed no matter app is in foreground or background
    }
}

and if you need to stop it use this method,

 - (IBAction)btnStopClicked:(UIButton *)sender {

    [self.updateTimer invalidate];
    self.updateTimer = nil;
    if (self.backgroundTask != UIBackgroundTaskInvalid)
    {
        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
        self.backgroundTask = UIBackgroundTaskInvalid;
    }
    i = 0;
}
Ariel Magbanua
  • 3,083
  • 7
  • 37
  • 48
Rishabh Srivastava
  • 3,683
  • 2
  • 30
  • 58
6

Check GCD for more information.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //code in background
    });
Boran
  • 949
  • 10
  • 17
0

A really easy way to accomplish what you want:

-(IBAction)btnStartClicked:(UIButton *)sender {
   [self performSelectorInBackground:@selector(codeInBakground) withObject:nil];
}

-(void)codeInBakground
{
   for (int i = 0; i < 100; i++) {
         NSLog(@"current progress %d", i);
        [NSThread sleepForTimeInterval:1]; //the code will print one number in each second, until 100
    }
}

In this way, your main thread, and your UI won't be blocked.

Lucas Eduardo
  • 11,525
  • 5
  • 44
  • 49
  • performSelector doesn't play nicely with ARC, the compiler doesn't know what the return type is. – Abizern Jul 31 '13 at 12:03
  • could you be more specific about a possible problem, or give some source? I have used a code similar to this once, and no problems/bugs/leaks were found. – Lucas Eduardo Jul 31 '13 at 12:10
  • For example http://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown I see this because the only time I use performSelector is when I'm using a string representation. For everything else I use GCD. As a second point, in your case you are passing a nil object to a selector that doesn't take a parameter. In this case you could use `performSelector:` instead of `performSelector:withObject:`. In fact you don't even need performSelector here, you could just use dispatch_async() and run what you have in the method as a block. – Abizern Jul 31 '13 at 12:32
  • First, I see your point, but I do not see any problems with the response of the link and this case, it's not returning anything or something like this. The point is `performSelectorInBackground` it's a lot easier to read and to use than GCD. Second, `performSelectorInBackground:` doens't exist, just `performSelectorInBackground:withObject:`, that's why I passed nil in this case. – Lucas Eduardo Jul 31 '13 at 12:41
  • In my experience GCD is a lot easier to read. The method to be called is right there, written in the context of the call, with perform selector you need to find the method that is elsewhere in the source. Also, because blocks capture variables in scope, you don't have to mess about with passing parameters about, the block can use them directly. In this case, you don't even need to do any of this, a better solution would be to just use a timer. – Abizern Jul 31 '13 at 12:47
  • I think his point, it's not print number 1 to 100, this was maybe just some "helloWorld for threads" to see if it was working. And I do agree that GCD it's a better way for experienced programmers, principally because the variable scope as you pointed, but for begginers, I still prefer `performSelectorInBackground:withObject:`, because GCD has this ugly parameters that you have to perform a little research to understand what they do and what you can do. You always, of course, use as a black box without knowing what's going on, but I'm not a fan of this way. – Lucas Eduardo Jul 31 '13 at 12:53