1

An App I'm developing pulls down a product catalogue (will be approx 40,000 products) via an API and parses them into CoreData so they can be easily searched through.

I have this method to handle the parsing of the data, downloading it works perfectly and as intended. I have a download progress bar and a processing progress bar. The download one works as required, but the label (or progress bar) doesn't change to the processing text or percentage progress. I've checked programmatically and it does output the correct label text to the console, it just doesn't show in the view.

Having checked using the activity monitor, the phone is maxing out CPU wise.. so I'm guessing that's why none of the on-view changes are being shown. Is there anyway I can reduce the computational load and get it showing the progress bar properly? I'm sure this is far from efficient code anyway, I haven't quite go to the stage where I know best practices etc..

Modified to use loop GCD

I've changed the code to make use of GCD, however this is giving me an error when run though: * Terminating app due to uncaught exception 'NSGenericException', reason: '* Collection <__NSCFSet: 0x8b26720> was mutated while being enumerated.'

- (void) processUpdatesBG {

    NSArray *jsonArray=[NSJSONSerialization JSONObjectWithData:_responseData options:0 error:nil];
    NSArray *products = [jsonArray valueForKey:@"products"];

    NSInteger productDBCount = _productDBCount;
    productDBCount = 0;

    AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *managedObjectContext = delegate.managedObjectContext;
    _managedObjectContext = managedObjectContext;

    self.totalCount = [products count];

    for (id product in products) {

        dispatch_queue_t processTheUpdates = dispatch_queue_create("com.app.process_the_updates", 0);



            NSFetchRequest *request = [[NSFetchRequest alloc] init];
            [request setEntity:[NSEntityDescription entityForName:@"Products" inManagedObjectContext:_managedObjectContext]];
            [request setIncludesSubentities:NO];


            NSPredicate *predicate = [NSPredicate predicateWithFormat:@"codes == %@", [product valueForKey:@"product_codes"]];
            [request setPredicate:predicate];



         dispatch_async(processTheUpdates,  ^{
             NSError *err;
             NSArray *results = [_managedObjectContext executeFetchRequest:request error:&err];
            if (results.count == 0){
                // Product doesn't exist with code, make a new product

                NSLog(@"Product.. %@", [product valueForKey:@"product_name"]);

                NSManagedObject* newProduct;
                newProduct = [NSEntityDescription insertNewObjectForEntityForName:@"Products" inManagedObjectContext:_managedObjectContext];

                [newProduct setValue:[product valueForKey:@"product_name"] forKey:@"name"];
                [newProduct setValue:[product valueForKey:@"product_codes"] forKey:@"codes"];

                if ([product valueForKey:@"information"] == (id)[NSNull null]){
                    // No information, NULL
                    [newProduct setValue:@"" forKey:@"information"];
                } else {
                    NSString *information = [product valueForKey:@"information"];
                    [newProduct setValue:information forKey:@"information"];

                }

            } else {

                // Product exists, update existing product
                for (NSManagedObject *r in results) {
                    [r setValue:[product valueForKey:@"product_name"] forKey:@"name"];

                    if ([product valueForKey:@"information"] == (id)[NSNull null]){
                        // No information, NULL
                        [r setValue:@"" forKey:@"information"];
                    } else {
                        NSString *information = [product valueForKey:@"information"];
                        [r setValue:information forKey:@"information"];
                    }

                }


            }

            dispatch_async(dispatch_get_main_queue(), ^{
                self.productDBCount = productDBCount + 1;
                NSNumber *progress = [NSNumber numberWithFloat:(self.productDBCount / self.totalCount)];
                self.downloadUpdateProgress.progress = [progress floatValue];
                NSLog(@"Added product");
            });

        });



    }

    NSError *error;

    if ([self.managedObjectContext save:&error]) {
        NSLog(@"Database Updated");
    } else {
        NSLog(@"Database not Updated, Error: %@", error);
    }


    self.updateStatus.text = @"Update Completed!";
    self.downloadUpdateProgress.hidden = YES;
    self.close.hidden = NO;

    //    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    //    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSDate *now = [[NSDate alloc] init];
    //    NSString *currentTimestamp = [dateFormatter stringFromDate:now];
    //    NSLog(@"%@", currentTimestamp);


    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    NSString *apiUpdateTimestamp = [jsonArray valueForKey:@"last_updated"];
    [prefs setObject:now forKey:@"last_downloaded_update"];
    [prefs setObject:apiUpdateTimestamp forKey:@"api_update_timestamp"];
    [prefs synchronize];
    // Set the lastDownloadedTimestamp as today
    // Set the last

}

Original Code

- (void) processUpdates {
    self.updateStatus.text = @"Processing Updates";
    self.downloadUpdateProgress.progress = 0;

    NSArray *jsonArray=[NSJSONSerialization JSONObjectWithData:_responseData options:0 error:nil];
    NSArray *products = [jsonArray valueForKey:@"products"];

    NSInteger productDBCount = 0; 

    AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *managedObjectContext = delegate.managedObjectContext;
    _managedObjectContext = managedObjectContext;

    NSInteger totalCount = [products count];

    for (id product in products) {
        NSFetchRequest *request = [[NSFetchRequest alloc] init];
        [request setEntity:[NSEntityDescription entityForName:@"Products" inManagedObjectContext:_managedObjectContext]];
        [request setIncludesSubentities:NO];
        NSError *err;
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"codes == %@", [product valueForKey:@"product_codes"]];
        [request setPredicate:predicate];

        NSArray *results = [_managedObjectContext executeFetchRequest:request error:&err];

        if (results.count == 0){
        // Product doesn't exist with code, make a new product

            NSManagedObject* newProduct;
            newProduct = [NSEntityDescription insertNewObjectForEntityForName:@"Products" inManagedObjectContext:_managedObjectContext];

            [newProduct setValue:[product valueForKey:@"product_name"] forKey:@"name"];
            [newProduct setValue:[product valueForKey:@"product_codes"] forKey:@"codes"];

            if ([product valueForKey:@"information"] == (id)[NSNull null]){
                // No information, NULL
                [newProduct setValue:@"" forKey:@"information"];
            } else {
                NSString *information = [product valueForKey:@"information"];
                [newProduct setValue:information forKey:@"information"];

            }                  

        } else {

            // Product exists, update existing product
            for (NSManagedObject *r in results) {
                [r setValue:[product valueForKey:@"product_name"] forKey:@"name"];

                if ([product valueForKey:@"information"] == (id)[NSNull null]){
                    // No information, NULL
                    [r setValue:@"" forKey:@"information"];
                } else {
                    NSString *information = [product valueForKey:@"information"];
                    [r setValue:information forKey:@"information"];                    
                }

            }

        }

        productDBCount = productDBCount + 1;
        NSNumber *progress = [NSNumber numberWithFloat:(productDBCount / totalCount)];
        self.downloadUpdateProgress.progress = [progress floatValue];

    }

    NSError *error;

    if ([self.managedObjectContext save:&error]) {
        NSLog(@"Database Updated");        
    } else {
        NSLog(@"Database not Updated, Error: %@", error);
    }


    self.updateStatus.text = @"Update Completed!";
    self.downloadUpdateProgress.hidden = YES;
    self.close.hidden = NO;

    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSDate *now = [[NSDate alloc] init];
    NSString *currentTimestamp = [dateFormatter stringFromDate:now];
    NSLog(@"%@", currentTimestamp);


    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    NSString *apiUpdateTimestamp = [jsonArray valueForKey:@"last_updated"];
    [prefs setObject:currentTimestamp forKey:@"last_downloaded_update"];
    [prefs setObject:apiUpdateTimestamp forKey:@"api_update_timestamp"];
    [prefs synchronize];
    // Set the lastDownloadedTimestamp as today
    // Set the last

}
4

3 回答 3

2

您需要在多个线程上运行以释放主线程以进行 UI 工作。可悲的是,多线程上的核心数据并非微不足道。此链接演示了实现此http://www.cocoanetics.com/2012/07/multi-context-coredata/的绝妙方法

于 2013-06-12T11:05:36.063 回答
1

您可以尝试在后台进行获取活动。

dispatch_queue_t <your dispatch>;//do it in .m before @implementation

然后

<your dispatch>=dispatch_queue_create("name", nil);//create your dispatch

然后编码

dispatch_async(myDispatch_photosView, ^{<your code>});

这将防止您的视图滞后

于 2013-06-12T09:31:05.790 回答
0

我带着这样的东西去了。我找不到一个有效的例子,所以希望这对未来的人有所帮助。非常基本,但你应该能够得到 jist。这是我最终选择的解决方案。

dispatch_queue_t backgroundDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

for (id p in products) {

  // Dispatch the following code on our background queue
  dispatch_async(backgroundDispatchQueue,
    ^{
        id product = p;
        // Because at this point we are running in another thread we need to create a
        // new NSManagedContext using the app's persistance store coordinator

        NSManagedObjectContext *backgroundThreadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
        [backgroundThreadContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];

        /*
        *
        *
        * Perform fetch and other actions as required.
        *
        *
        */

        dispatch_async(dispatch_get_main_queue(), ^
        {

            self.productDBCount = self.productDBCount + 1;
            float progress = ((float)self.productDBCount / (float)self.totalCount);
            int percent = progress * 100.0f;
            self.downloadUpdateProgress.progress = progress;
            self.percentageComplete.text = [NSString stringWithFormat:@"%i", percent];

        });

      });
}
于 2013-06-19T07:22:09.687 回答