使用文件(CSV)而不是使用CoreData

时间:2013-06-19 07:27:55

标签: iphone ios objective-c

我正在构建的应用程序包含数千个项目的目录,这些项目需要存储在手机上。目前我通过CoreData实现这一目标,从逻辑上讲,它似乎是最好的地方。我正在使用GCD在后台运行CoreData插入流程并显示进度条/当前百分比完成情况。这可以按预期工作,但是对于仅5000件物品,在iPhone 4上完成需要8分钟。该应用程序将在3GS及更高版本上使用,并且一旦启动就更有可能包含30/40,000件物品。因此,这个处理时间将非常长。

有什么方法可以使用CSV文件或其他东西进行搜索,而不是将每个项目存储在CoreData中?我假设这样的方法有一些效率下降,但它会减轻过多的等待时间。除非有另一种解决方案可以帮助解决这个问题。

感谢。

修改: 我不确定如何在整个操作结束时保存上下文,因为它在循环中使用单独的上下文。对此的任何建议将非常感谢。我不知道如何进步。

正在使用插入代码

- (void) processUpdatesGCD {
    NSArray *jsonArray=[NSJSONSerialization JSONObjectWithData:_responseData options:0 error:nil];
    NSArray *products = [jsonArray valueForKey:@"products"];
    NSArray *deletions;
    if ([jsonArray valueForKey:@"deletions"] == (id)[NSNull null]){
        self.totalCount = [products count];
    } else {
        deletions = [jsonArray valueForKey:@"deletions"];
        self.totalCount = [products count] + [deletions count];
    }

    self.productDBCount = 0;

    _delegate = [[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *managedObjectContext = _delegate.managedObjectContext;
    self.persistentStoreCoordinator = [managedObjectContext persistentStoreCoordinator];
    _managedObjectContext = managedObjectContext;



    // Create a new background queue for GCD
    dispatch_queue_t backgroundDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

    for (id p in products) {

//        id product = p;

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

                           NSFetchRequest *BGRequest = [[NSFetchRequest alloc] init];
                           NSLog(@"Running.. (%@)", product);
                           [BGRequest setEntity:[NSEntityDescription entityForName:@"Products" inManagedObjectContext:backgroundThreadContext]];
                           [BGRequest setIncludesSubentities:NO];

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

                           NSError *err;
                           NSArray *results = [backgroundThreadContext executeFetchRequest:BGRequest error:&err];

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

                               NSLog(@"Product not found for add/update (%@)", [product valueForKey:@"product_name"]);

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

                               [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 {
                               NSLog(@"Product found for add/update (%@)", [product valueForKey:@"product_name"]);
                               // 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"];
                                   }

                               }

                           }



                           // Is very important that you save the context before moving to the Main Thread,
                           // because we need that the new object is writted on the database before continuing
                           NSError *error;

                           if(![backgroundThreadContext save:&error])
                           {
                               NSLog(@"There was a problem saving the context (add/update). With error: %@, and user info: %@",
                                     [error localizedDescription],
                                     [error userInfo]);
                           }


                           // Now let's move to the main thread
                           dispatch_async(dispatch_get_main_queue(), ^
                                          {
                                              // If you have a main thread context you can use it, this time i will create a
                                              // new one
//                                              NSManagedObjectContext *mainThreadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
//                                              [mainThreadContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];

                                              self.productDBCount = self.productDBCount + 1;                                              
                                              float progress = ((float)self.productDBCount / (float)self.totalCount);
                                              int percent = progress * 100.0f;
//                                              NSNumber *progress = [NSNumber numberWithFloat:((float)self.productDBCount / (float)self.totalCount)];
                                              self.downloadUpdateProgress.progress = progress;
                                              self.percentageComplete.text = [NSString stringWithFormat:@"%i", percent];
                                              NSLog(@"Added / updated product %f // ProductDBCount: %i // Percentage progress: %i // Total Count: %i", progress, self.productDBCount, percent, self.totalCount);
                                              if (self.productDBCount == self.totalCount){
                                                  [self updatesCompleted:[jsonArray valueForKey:@"last_updated"]];
                                              }

                                          });
                       });

    }


    if ([deletions count] > 0){
        for (id d in deletions){
            dispatch_async(backgroundDispatchQueue,
                           ^{
                               id deleted = d;
                               // 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];

                               NSFetchRequest *BGRequest = [[NSFetchRequest alloc] init];
//                               NSLog(@"Running.. (%@)", deleted);
                               [BGRequest setEntity:[NSEntityDescription entityForName:@"Products" inManagedObjectContext:backgroundThreadContext]];
                               [BGRequest setIncludesSubentities:NO];

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

                               NSError *err;
                               NSArray *results = [backgroundThreadContext executeFetchRequest:BGRequest error:&err];

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

                                   NSLog(@"Product not found, can't delete.. %@", [deleted valueForKey:@"product_name"]);

                               } else {
                                   NSLog(@"Product found, deleting: %@", [deleted valueForKey:@"product_name"]);
                                   // Product exists, update existing product
                                   for (NSManagedObject *r in results) {
                                       [backgroundThreadContext deleteObject:r];
                                   }

                               }



                               // Is very important that you save the context before moving to the Main Thread,
                               // because we need that the new object is writted on the database before continuing
                               NSError *error;

                               if(![backgroundThreadContext save:&error])
                               {
                                   NSLog(@"There was a problem saving the context (delete). With error: %@, and user info: %@",
                                         [error localizedDescription],
                                         [error userInfo]);
                               }


                               // Now let's move to the main thread
                               dispatch_async(dispatch_get_main_queue(), ^
                                              {

                                                  self.productDBCount = self.productDBCount + 1;
                                                  float progress = ((float)self.productDBCount / (float)self.totalCount);
                                                  int percent = progress * 100.0f;
                                                  //                                              NSNumber *progress = [NSNumber numberWithFloat:((float)self.productDBCount / (float)self.totalCount)];
                                                  self.downloadUpdateProgress.progress = progress;
                                                  self.percentageComplete.text = [NSString stringWithFormat:@"%i", percent];
                                                  NSLog(@"Deleted product %f // ProductDBCount: %i // Percentage progress: %i // Total Count: %i", progress, self.productDBCount, percent, self.totalCount);
                                                  if (self.productDBCount == self.totalCount){
                                                      [self updatesCompleted:[jsonArray valueForKey:@"last_updated"]];
                                                  }

                                                  /*
                                                   *
                                                   * Change the completion changes to a method. Check to see if the total number of products == total count. If it does, run the completion method. 
                                                   *
                                                   */

                                              });
                           });
        }
    }


}

将IF放入调度中,最后运行一次保存

    // Create a new background queue for GCD
dispatch_queue_t backgroundDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    //        id product = p;

    // Dispatch the following code on our background queue
  dispatch_async(backgroundDispatchQueue,
    ^{

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

      for (id p in products) {
        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



        NSFetchRequest *BGRequest = [[NSFetchRequest alloc] init];
        NSLog(@"Running.. (%@)", product);
        [BGRequest setEntity:[NSEntityDescription entityForName:@"Products" inManagedObjectContext:backgroundThreadContext]];
        [BGRequest setIncludesSubentities:NO];

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

        NSError *err;
        NSArray *results = [backgroundThreadContext executeFetchRequest:BGRequest error:&err];

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

          NSLog(@"Product not found for add/update (%@)", [product valueForKey:@"product_name"]);

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

          [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 {
          NSLog(@"Product found for add/update (%@)", [product valueForKey:@"product_name"]);
          // 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"];
            }

          }

        }



      // Is very important that you save the context before moving to the Main Thread,
      // because we need that the new object is writted on the database before continuing


      // Now let's move to the main thread
        dispatch_async(dispatch_get_main_queue(), ^
        {
      // If you have a main thread context you can use it, this time i will create a
      // new one
      //                                              NSManagedObjectContext *mainThreadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
      //                                              [mainThreadContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];


          self.productDBCount = self.productDBCount + 1;
          float progress = ((float)self.productDBCount / (float)self.totalCount);
          int percent = progress * 100.0f;
      //                                              NSNumber *progress = [NSNumber numberWithFloat:((float)self.productDBCount / (float)self.totalCount)];
          self.downloadUpdateProgress.progress = progress;
          self.percentageComplete.text = [NSString stringWithFormat:@"%i", percent];
          NSLog(@"Added / updated product %f // ProductDBCount: %i // Percentage progress: %i // Total Count: %i", progress, self.productDBCount, percent, self.totalCount);

          NSDate *currentProcessedDate = [NSDate date];
          NSTimeInterval timeSinceStarted = [currentProcessedDate timeIntervalSinceDate:self.startProcessing];
          NSInteger remainingProcesses = self.totalCount - self.productDBCount;
          float timePerProcess = timeSinceStarted / (float)self.productDBCount;
          float remainingTime = timePerProcess * (float)remainingProcesses;
          self.timeRemaining.text = [NSString stringWithFormat:@"ETA: %0.0f minutes", fmodf(remainingTime, 60.0f)];

          if (self.productDBCount == self.totalCount){
            [self updatesCompleted:[jsonArray valueForKey:@"last_updated"]];
          }

      /*
      *
      * Change the completion changes to a method. Check to see if the total number of products == total count. If it does, run the completion method. 
      *
      */
    });

  }

    NSError *error;

        if(![backgroundThreadContext save:&error])
        {
          NSLog(@"There was a problem saving the context (add/update). With error: %@, and user info: %@",
            [error localizedDescription],
            [error userInfo]);
        }


    });

2 个答案:

答案 0 :(得分:7)

好的,这是你的问题。

每次插入记录时,都会对上下文执行保存操作。 现在,不要这样做,这就需要很多时间。

在循环结束时,不是每次插入记录时都执行一次保存操作。

答案 1 :(得分:0)

在你的情况下,我会检查什么是真正耗时的?

是下载数据,是否将数据导入CoreData?

您从哪里获取数据?你下载它还是在Application Bundle中有它?

CoreData比CSV文件更快。所以它不会让你的应用更快。

一些技巧:

  • 导入数据时,只需在流程结束时保存上下文。不要在循环中保存上下文。

  • 如果您不需要下载数据并且可以放入数据包,您可以在模拟器中创建coredata文件,放入数据包并在首次启动时复制文件。它比导入数据要快得多。