ios - 如何定期保存ManagedObjectContext?目前正在丢弃添加的 1/3 对象

标签 ios objective-c cocoa-touch core-data

我试图以 1000 为间隔保存 MOC,但是下面的代码似乎错过了其中的一些。这只拯救了2/3的物体,剩下的就好像消失在深渊里一样。这似乎是实现这一目标的一种笨拙的方法,所以如果有人能提出更好的方法,我很乐意听到。

此方法还需要大约 9 分钟来添加 115,000 个对象。我可以做些什么来改善这一点吗?谢谢。

dispatch_async(backgroundDispatchQueue,{

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


 [products enumerateObjectsUsingBlock:^(id product, NSUInteger idx, BOOL *stop) {


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

   }

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

   }

   if ((self.productDBCount % 1000) == 0){
     NSLog(@"SAVE ME");
     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]);
     }
   } else if ((self.productDBCount + 1) == self.totalCount){
     NSLog(@"Final Save");

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



   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];
    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 %0.0f seconds", (((float)remainingTime - fmodf(remainingTime, 60.0f))/60), fmodf(remainingTime, 60.0f)];

    if (self.productDBCount == self.totalCount){

      [self updatesCompleted:[jsonArray valueForKey:@"last_updated"]];
    }

                                                 });
}];

});

最佳答案

在后台队列上调度的 block 的末尾,您可以在主队列上异步调度另一个 block 。在那里你增加你的 self.productDBCount 。但同时您在后台队列上使用此属性。

因此,您在循环的下一次迭代中的后台 block 可以获得不递增的值(因为主队列上的 block 尚未执行)并做出错误的决定。更糟糕的是,如果这个属性被声明为非原子的,它甚至可能会得到一个损坏的值(尽管使它成为原子的无论如何也不能解决这个竞争条件)。

尝试用同步调用替换异步调用:dispatch_sync(dispatch_get_main_queue()...

为了使导入更容易占用内存,您可能还需要在保存每个批处理后重置上下文。但您需要确保重置后不使用在此上下文中创建的任何对象。

如果您想查看导入过程中什么花费了大部分时间,请在 Instruments 中运行它。我可以推荐 Core Data 仪器与 Time Profiler 仪器的组合。核心数据工具仅在模拟器中可用。

关于ios - 如何定期保存ManagedObjectContext?目前正在丢弃添加的 1/3 对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17613275/

相关文章:

iphone - 向全屏电影添加自定义控件

iphone - 如何在 iOS SDK 中使用 Glyph 获取 UIBezierpath

didReceiveMemoryWarning 后 iOS UITableView 空白

iphone - UIButton:设置选定突出显示状态的图像

ios - 返回按钮隐藏在导航栏中(但仍然有效)

iOS 如何启动图像选择器

ios - CoreBluetooth : number of bytes sent ! = 接收到的字节数

iphone - 如何在以编程方式创建的 UILabel 上添加 padding-left?

ios - 获取使用 WKWebView 点击的所有项目的 URL

ios - 架构 i386 : "_OBJC_CLASS_$_ZipException", 的 undefined symbol 引用自:错误