18

I'm a new in iOS. I'm working on the app needs to run a task for getting data from Server in the background thread as i don't want to lock the UI in main thread. This task will take a long time, I tried using the NSTimer but it is still lock the UI. My task is to check new messages in Chat screen, I need to call this task every 5s. If I use NSTimer, when input text , the text seems to freeze a moment while this task is performing. Is there any way to process this task without lock UI. Please give me some advices. Thanks so much.

== UPDATE CODE ==

 - (void)performBackgroundTask
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //Do background work

        if([[NSUserDefaults standardUserDefaults] boolForKey:@"LoggedIn"]) {
            NSDictionary * userDictionary = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"SessionDictionary"];

            NSString *authenKey= [userDictionary valueForKey:@"authToken"];

            NSString* limit = @"1000";

            [[LSDataManager sharedDataManager] getLatestMessagesWithAuthKey:authenKey andLimit:limit withBlock:^ (NSDictionary* responseDict)
             {
                 if (responseDict) {
                     [self loadDataFromServer:responseDict];

                     NSArray* lastMessageArray= nil;

                     //filter message data
                     if (self.MessagesArray.count >0) {

                         if (!self.isSeller) {

                             lastMessageArray = [self filterMessageData:self.MessagesArray withProductID:self.productID withSellerID:self.receiverID withBuyerID:self.senderID];
                         }
                         else
                         {
                             lastMessageArray = [self filterMessageData:self.MessagesArray withProductID:self.productID withSellerID:self.senderID withBuyerID:self.receiverID];
                         }

                         NSLog(@"filter array %@",lastMessageArray);

                         if([lastMessageArray count] >0){
                             //[self loadMessages:lastMessageArray];
                             if (self.TempdataSource == nil) {
                                 self.TempdataSource = [NSMutableArray array];
                             }
                             else
                             {
                                 [self.TempdataSource removeAllObjects];
                             }

                             self.TempdataSource = [[[ContentManager sharedManager] generateConversation:lastMessageArray withSenderID:self.senderID] mutableCopy];

                         }
                     }
                 }
             }];
        }


        dispatch_async(dispatch_get_main_queue(), ^{
            //Update UI

            //compare 2 arrays
            if ([self.TempdataSource count] == [self.dataSource count]) {
                NSLog(@"both are same");
            }
            else{
                NSLog(@"both are different");
                self.dataSource = [self.TempdataSource mutableCopy];

                [self refreshMessages];
            }

        });
    });
}
NTNT
  • 541
  • 1
  • 7
  • 18
  • I'm sorry. I can't give you an answer for you question. But I can just say that using a NSTimer to call a task every 5s is resource consuming and I wouldn't advice you to do so! – eliasah Sep 19 '14 at 17:22
  • @eliasah I'm not sure why you think NSTimer is resource-intensive. Do you have any references? Or are you just referring to the idea of doing anything once every 5 seconds? – David Berry Sep 19 '14 at 17:46

2 Answers2

33

Scheduling the task using an NSTimer is indeed the right way to go. You just need to make sure you're running your heavy non-UI code on a background thread. Here's an example

- (void)viewDidLoad {
    [super viewDidLoad];    
    [self startTimedTask];
}

- (void)startTimedTask
{
    NSTimer *fiveSecondTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(performBackgroundTask) userInfo:nil repeats:YES];
}

- (void)performBackgroundTask
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //Do background work
        dispatch_async(dispatch_get_main_queue(), ^{
            //Update UI
        });
    });
}
Stavash
  • 14,244
  • 5
  • 52
  • 80
  • 1
    Thanks but I heard that if use NSTimer is not good, right? Sorry to I'm a newbie in IOS, i just want to understand more – NTNT Sep 19 '14 at 17:30
  • Can you add a reference to where you've heard this? – Stavash Sep 19 '14 at 17:31
  • 1
    @Stavash I said that NSTimer to active a task is resource consuming. I didn't say that it's not good. But I totally agree with the solution that you gave +1. – eliasah Sep 19 '14 at 17:32
  • 2
    As long as you use it right, it's the right class for the job. Keep in mind that it needs to be scheduled on the main runloop and that it maintains a strong reference to its target (in this case, self). – Stavash Sep 19 '14 at 17:34
  • @Stavash: Can I have a question? If I want to run this task in background thread for all app. I mean this task still running even any Viewcontroller is active – NTNT Sep 19 '14 at 17:39
  • Create a singleton class that is initialized upon application launch. The timed process should run in your singleton class (also define and launch your NSTimer from that instance) – Stavash Sep 19 '14 at 17:46
  • If you really want it running all the time, my recommendation would be to create an NSThread and handle it there, it might be a cleaner implementation than using NSTimer. – David Berry Sep 19 '14 at 17:48
  • @Stavash: I tried your code but when I input some text in textfield. The text still freeze when running the task. – NTNT Sep 19 '14 at 17:55
  • It will be more intuitive than NSTimer. NSTimer will be less resource intensive, but a while loop with a sleep at the bottom is more readable than an NSTimer that fires off some random function periodically. – David Berry Sep 19 '14 at 17:55
  • @Stavash: I have updated my question with the code I tried. It seem to be freeze in main thread. How can I fix? – NTNT Sep 19 '14 at 17:57
  • What's happening in your "refreshMessages" method? The problem is that you're calling a resource-intense operation from the main thread – Stavash Sep 20 '14 at 19:55
  • @Stavash: The "refreshMessages" method will refresh the message tableview. As far as I know, i need to reload tableview in main thread. I'm checking if there are new message,the message tableview will reload data. – NTNT Sep 21 '14 at 07:11
  • The "reloadData" call to the table view is indeed meant to be called on the main thread but I have a feeling you're calling a web service on it before (the "I'm checking if there are new message" you said sounds suspicious). – Stavash Sep 21 '14 at 07:16
  • @Stavash kindly have a look at http://stackoverflow.com/questions/40504453/background-thread-to-call-methods – Chaudhry Talha Nov 09 '16 at 10:14
17
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    //Background Thread
    dispatch_async(dispatch_get_main_queue(), ^(void){
        //Run UI Updates
    });
});

Try this

dmerlea
  • 894
  • 4
  • 12