2

私が構築しているアプリには、電話に保存する必要がある何千ものアイテムのカタログが含まれています。現在、私は CoreData を介してこれを達成しています。論理的には、それを配置するのに最適な場所のように思えたからです。GCD を使用して CoreData 挿入プロセスをバックグラウンドで実行し、進行状況バー/現在の完了率を表示しています。これは期待どおりに機能しますが、5000 アイテムのみの場合、iPhone 4 で完了するのに 8 分かかります。このアプリケーションは 3GS 以降で使用され、起動すると 30/40,000 のアイテムが含まれる可能性が高くなります。したがって、この処理時間は恐ろしく長くなります。

各項目を CoreData に保存する代わりに、CSV ファイルなどを使用して検索する方法はありますか? このようなアプローチでは効率が低下すると思いますが、過度の待ち時間は軽減されます。この問題に役立つ別の解決策がない限り。

ありがとう。

編集:ループ内で別のコンテキストを使用するため、操作全体の最後にコンテキストを保存する方法がわかりません。これに対する提案は非常に高く評価されます。私はこれをどのように進めるか分かりません。

使用中の挿入コード

- (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 を配置し、最後に 1 つの保存を実行します

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


    });
4

2 に答える 2

7

わかりました、これがあなたの問題です。

レコードを挿入するたびに、コンテキストに保存操作を行います。今、それをしないでください。それには多くの時間がかかります。

保存操作は、レコードを挿入するたびにではなく、ループの最後に 1 回実行してください。

于 2013-06-19T07:46:16.463 に答える
0

あなたの場合、本当に時間がかかるのは何ですか?

データをダウンロードしていますか、CoreData にデータをインポートしていますか?

どこからデータを取得しますか? ダウンロードしますか、それとも Application Bundle に含まれていますか?

CoreData は CSV ファイルより高速です。したがって、アプリが高速になることはありません。

いくつかのトリック:

  • データのインポート中は、プロセスの最後にコンテキストを保存するだけです。コンテキストをループに保存しないでください。

  • データをダウンロードする必要がなく、バンドルに入れることができる場合は、シミュレーターでコアデータ ファイルを作成し、バンドルに入れて、最初の起動時にファイルをコピーできます。データをインポートするよりもはるかに高速です。

于 2013-06-19T07:37:15.270 に答える