简体   繁体   中英

IOS: Main thread slowing down during background operation

I am using the following code to conduct what I want to be a background sync but the main thread is slowing down or even coming to a halt when the json received is larger than 20 or so records. Is there anything wrong with this code for a background operation? What could be blocking the main thread. Thank you for any suggestions.

Note there is a commented out line below performSelectorOnMainThread where the app processes the JSON received that I changed to yet another background thread but the change does not seem to help.

#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) //1
#define kProductsURL [NSURL URLWithString: @"http://~/getproducts.php"]

//in viewDidLoad
if(hasInternet==YES && [loggedIntoServer isEqual:@1]) {

        dispatch_async(kBgQueue, ^{
            NSData* data = [NSData dataWithContentsOfURL: kProductsURL];
              //previous line grabed data from api.
            if (data) {
         //   [self performSelectorOnMainThread:@selector(fetchData:) withObject:data waitUntilDone:YES];//no longer doing this on main thread
                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                    [self fetchData:data];
                });

                }
        });
              ;
    } //close hasInternet, logged into server.



  - (void)fetchData:(NSData *)jsonFeed {
    NSError* error;
    NSDictionary* json = [NSJSONSerialization JSONObjectWithData:jsonFeed
                                                         options:kNilOptions
                                                           error:&error];
    NSMutableArray* latestProducts = [[NSMutableArray alloc] init];
    //this is specific to format of JSON
    if (![[json objectForKey:@“products"] isKindOfClass:[NSNull class]]) {
            latestProducts = [[json objectForKey:@“products"]mutableCopy];
    getProducts = latestProducts;
    int size = [latestProducts count];
    [self.tableView reloadData];
    getProducts = [self convertFeedtoObject:latestProducts];
    [self importAndSaveProducts:getProducts];//this imports and saves
    self.recentlySynced=YES;
     }
}

You do not need to have a nested call to the same queue. Also you should do any UI work on the main thread. For more information look at Apple's Concurrency Programming Guide

In your fetchData method load your table like this.

dispatch_async(dispatch_get_main_queue(), {
    // Your UI work 
    [self.tableView reloadData];

})


 // Remove second dispatch_async call 

 //in viewDidLoad
if(hasInternet==YES && [loggedIntoServer isEqual:@1]) {

        dispatch_async(kBgQueue, ^{
            NSData* data = [NSData dataWithContentsOfURL: kProductsURL];
              //previous line grabed data from api.
            if (data) {
                [self fetchData:data];

                }
        });
              ;
} //close hasInternet, logged into server.

You just did something redundant. You dispatched the fetching of data in a background thread. But then you also did the [self.tableView reloadData]; in the background thread. That's why your UI will be affected.

Try this:

if(hasInternet==YES && [loggedIntoServer isEqual:@1]) 
{
    dispatch_async(kBgQueue, ^
    {
        NSData* data = [NSData dataWithContentsOfURL: kProductsURL];

        if (data) 
        {
              dispatch_async(dispatch_get_main_queue(), ^
              {
                  [self fetchData:data];
              });
        }
    });
}

What I did is I changed this part of your code:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                    [self fetchData:data];
                });

Because you should only do any changes to UI in the main thread. and this part of my code is doing the job in main thread.

dispatch_async(dispatch_get_main_queue(), ^
                  {
                      [self fetchData:data];
                  });

There are several errors in your original code, change to the following:

#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) //CHANGE TO BACKGROUND
#define kProductsURL [NSURL URLWithString: @"http://~/getproducts.php"]

//in viewDidLoad
if(hasInternet==YES && [loggedIntoServer isEqual:@1]) {
        dispatch_async(kBgQueue, ^{
            NSData* data = [NSData dataWithContentsOfURL: kProductsURL];
            if (data) {             
                [self fetchData:data];
            }
        });
    } //close hasInternet, logged into server.

Change the fetch data to the following:

- (void)fetchData:(NSData *)jsonFeed {
    NSError* error;
    NSDictionary* json = [NSJSONSerialization JSONObjectWithData:jsonFeed
                                                         options:kNilOptions
                                                           error:&error];

    NSMutableArray* latestProducts = [[NSMutableArray alloc] init];

    //this is specific to format of JSON
    if (![[json objectForKey:@"products"] isKindOfClass:[NSNull class]]) {

        latestProducts = [[json objectForKey:@"products"]mutableCopy];

        getProducts = latestProducts;
        int size = [latestProducts count];

        //Do this on the main thread:
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.tableView reloadData];
        });

        getProducts = [self convertFeedtoObject:latestProducts];
        [self importAndSaveProducts:getProducts];//this imports and saves
        self.recentlySynced=YES;
     }
}

Depending on how your table view works and what the data source is like, you may want to move the reload table view line (with the main queue dispatch) to underneath self.recentSynced = YES.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM