5000+ CoreData查询最大化iPhone CPU并滞后于显示器

时间:2013-06-12 09:16:37

标签: iphone ios objective-c

我正在开发的应用程序通过API下载产品目录(将是大约40,000种产品)并将它们解析为CoreData,以便可以轻松搜索它们。

我有这种方法来处理数据的解析,下载它可以完美地按照预期工作。我有一个下载进度条和一个处理进度条。下载可按要求工作,但标签(或进度条)不会更改为处理文本或百分比进度。我已经以编程方式检查了它确实将正确的标签文本输出到控制台,它只是没有在视图中显示。

使用活动监视器进行检查后,手机正在最大程度地降低CPU ...所以我猜这就是为什么没有显示任何视图上的更改。无论如何,我可以减少计算负荷并让它正确显示进度条吗?我确信这远不是有效的代码,我还没有完全进入我认识最佳实践的阶段等。

修改为使用循环GCD

我已经更改了代码以使用GCD,但是在运行时这会给我一个错误: *由于未捕获的异常'NSGenericException'而终止应用,原因:'* Collection&lt ; __ NSCFSet:0x8b26720>在被列举时被突变。'

- (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

}

原始代码

- (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

}

3 个答案:

答案 0 :(得分:2)

您需要在多个线程上运行以释放主线程以进行UI工作。遗憾的是,多线程上的核心数据并非易事。此链接演示了实现此http://www.cocoanetics.com/2012/07/multi-context-coredata/

的绝妙方式

答案 1 :(得分: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>});

这会阻止您的视图落后

答案 2 :(得分:0)

我选择了这样的东西。我找不到一个有效的例子,所以希望这将有助于将来的某个人。非常基本,但你应该能够得到这个主义。这是我最终选择的解决方案。

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];

        });

      });
}