2011-05-26 17 views
8

Birçok nesneye (liste öğesi) ilişkide tek bir nesneyi (liste) olan temel veri tabanlı bir uygulamam var. Aygıtlar arasında veri senkronizasyonu üzerinde çalışıyorum ve bunun bir parçası olarak arka plan iş parçacıklarındaki (bir NSOperation alt sınıfı aracılığıyla) XML dosyalarını listelerim.Çekirdek Verileri/NSOperation: Nesneleri numaralandırma ve silme işlemi sırasında çökme

Varolan bir listeyi güncellerken, tüm eski liste öğelerini (NSManagedObjectContext özelinden bu iş parçacığına) siliyorum ve bunları XML dosyasındaki yenileriyle değiştiriyorum ... silme, numaralandırma yoluyla işlenir o listede için öğeleri:

for (ListItemCD *item in listToUpdate.listItems) { 
    [self.importContext deleteObject:item]; 
} 

Ancak, arada bir, o numaralandırma sırasında bir kilitlenme alıyorum: '* Koleksiyonu:

app Sonlandırma

* nedeniyle yakalanmamış istisna 'NSGenericException', akıl için < _NSFaultingMutableSet: 0x4fcfcb0> bei iken mutasyona uğradı numaralandırıldı.

Bu sorunun nedenini aramaya başladığınızdan emin değilim. Listeyi, numaralandırma yapılırken kodun başka bir bölümünde değiştirmem. Aynı anda birden fazla iş parçacığı olabilir, farklı listeler içe aktarılır/güncelleştirilir ... başka bir iş parçacığı bağlamı kaydeder bir soruna neden olur - çünkü ana bağlamı da bildirir (eğer numaralandırma ile aynı anda gerçekleşirse) ?

- (void)main { 

    // input the xml data into GDataXML 
    NSData *xmlData = [[NSMutableData alloc] initWithContentsOfFile:self.filePath]; 
    NSError *error; 
    GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&error]; 



    // get the list name (so that I know which list to update) 
    NSString *listName; 
    NSArray *listNames = [doc.rootElement elementsForName:@"listName"]; 
    if (listNames.count > 0) { 
     GDataXMLElement *listNameElement = (GDataXMLElement *) [listNames objectAtIndex:0]; 
     listName = listNameElement.stringValue; 
     // NSLog(@"listName: %@", listName); 




     // perform a fetch to find the old list with the same name (if there is one) 
     NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
     NSEntityDescription *entity = [NSEntityDescription entityForName:@"SubListCD" inManagedObjectContext:self.importContext]; 
     [fetchRequest setEntity:entity]; 

     NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K like %@", @"listName", listName]; 
     [fetchRequest setPredicate:predicate]; 

     NSError *error; 
     NSArray *fetchedObjects = [self.importContext executeFetchRequest:fetchRequest error:&error]; 
     // NSLog(@"fetchedObjects count: %d", [fetchedObjects count]); 
     [fetchRequest release]; 



     /* 
     // if I found the list, update its data 
     */ 

     if ([fetchedObjects count] == 1) { 
      SubListCD *listToUpdate = [fetchedObjects objectAtIndex:0]; 

      // get the list icon name 
      NSArray *listIconNames = [doc.rootElement elementsForName:@"listIconName"]; 
      if (listIconNames.count > 0) { 
       GDataXMLElement *listIconNameElement = (GDataXMLElement *) [listIconNames objectAtIndex:0]; 
       NSString *listIconName = listIconNameElement.stringValue; 
       // NSLog(@"listIconName: %@", listIconName); 
       listToUpdate.listIconName = [NSString stringWithString:listIconName]; 
      } 

      // get the isChecklist BOOL 
      NSArray *isChecklistBools = [doc.rootElement elementsForName:@"isChecklist"]; 
      if (isChecklistBools.count > 0) { 
       GDataXMLElement *isChecklistElement = (GDataXMLElement *) [isChecklistBools objectAtIndex:0]; 
       NSString *isChecklist = isChecklistElement.stringValue; 
       // NSLog(@"isChecklist: %@", isChecklist); 
       listToUpdate.isCheckList = [NSNumber numberWithBool:[isChecklist isEqualToString:@"YES"]]; 
      } 

      // get the itemsToTop BOOL 
      NSArray *itemsToTopBools = [doc.rootElement elementsForName:@"itemsToTop"]; 
      if (itemsToTopBools.count > 0) { 
       GDataXMLElement *itemsToTopElement = (GDataXMLElement *) [itemsToTopBools objectAtIndex:0]; 
       NSString *itemsToTop = itemsToTopElement.stringValue; 
       // NSLog(@"itemsToTop: %@", itemsToTop); 
       listToUpdate.itemsToTop = [NSNumber numberWithBool:[itemsToTop isEqualToString:@"YES"]]; 
      } 

      // get the includeInBadgeCount BOOL 
      NSArray *includeInBadgeCountBools = [doc.rootElement elementsForName:@"includeInBadgeCount"]; 
      if (includeInBadgeCountBools.count > 0) { 
       GDataXMLElement *includeInBadgeCountElement = (GDataXMLElement *) [includeInBadgeCountBools objectAtIndex:0]; 
       NSString *includeInBadgeCount = includeInBadgeCountElement.stringValue; 
       // NSLog(@"includeInBadgeCount: %@", includeInBadgeCount); 
       listToUpdate.includeInBadgeCount = [NSNumber numberWithBool:[includeInBadgeCount isEqualToString:@"YES"]]; 
      } 

      // get the list's creation date 
      NSArray *listCreatedDates = [doc.rootElement elementsForName:@"listDateCreated"]; 
      if (listCreatedDates.count > 0) { 
       GDataXMLElement *listDateCreatedElement = (GDataXMLElement *) [listCreatedDates objectAtIndex:0]; 
       NSString *listDateCreated = listDateCreatedElement.stringValue; 
       // NSLog(@"listDateCreated: %@", listDateCreated); 
       listToUpdate.dateCreated = [self dateFromString:listDateCreated]; 
      } 

      // get the list's modification date 
      NSArray *listModifiedDates = [doc.rootElement elementsForName:@"listDateModified"]; 
      if (listModifiedDates.count > 0) { 
       GDataXMLElement *listDateModifiedElement = (GDataXMLElement *) [listModifiedDates objectAtIndex:0]; 
       NSString *listDateModified = listDateModifiedElement.stringValue; 
       // NSLog(@"listDateModified: %@", listDateModified); 
       listToUpdate.dateModified = [self dateFromString:listDateModified]; 
      } 



      // NOTE: it's okay to get the displayOrder from index.plist here, since these update operations aren't called until after index.plist is loaded from Dropbox 

      // get a reference to the documents directory 
      NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
      NSString *documentsDirectory = [paths objectAtIndex:0]; 

      // get the file path of the index.plist file 
      NSString *indexFilePath = [documentsDirectory stringByAppendingPathComponent:@"index.plist"]; 

      // build an array with the names of the lists in the index 
      NSMutableArray *listsIndexArray = [NSMutableArray arrayWithContentsOfFile:indexFilePath]; 

      int listIndex = [listsIndexArray indexOfObject:listName]; 

      listToUpdate.displayOrder = [NSNumber numberWithInt:listIndex]; 





      // remove the old list items from the listToUpdate, since I'll be adding them from scratch from the XML file 
      for (ListItemCD *item in listToUpdate.listItems) { 
       [self.importContext deleteObject:item]; 
      } 




      // get an array of the list items so I can add them all 
      NSArray *listItems = [doc.rootElement elementsForName:@"item"]; 
      if (listItems.count > 0) { 
       int counter = 0; 
       for (GDataXMLElement *item in listItems) { 

        // create the new item 
        ListItemCD *newItem = [NSEntityDescription insertNewObjectForEntityForName:@"ListItemCD" inManagedObjectContext:self.importContext]; 

        // item name 
        NSArray *itemNames = [item elementsForName:@"itemName"]; 
        if (itemNames.count > 0) { 
         GDataXMLElement *itemNameElement = (GDataXMLElement *) [itemNames objectAtIndex:0]; 
         NSString *itemName = itemNameElement.stringValue; 
         // NSLog(@"itemName: %@", itemName); 
         newItem.itemName = [NSString stringWithString:itemName]; 
        } else continue; 

        // item note 
        NSArray *itemNotes = [item elementsForName:@"itemNote"]; 
        if (itemNotes.count > 0) { 
         GDataXMLElement *itemNoteElement = (GDataXMLElement *) [itemNotes objectAtIndex:0]; 
         NSString *itemNote = itemNoteElement.stringValue; 
         // NSLog(@"itemNote: %@", itemNote); 
         newItem.itemNote = [NSString stringWithString:itemNote]; 
        } else continue; 

        // itemReadOnly BOOL 
        NSArray *itemReadOnlyBools = [item elementsForName:@"itemReadOnly"]; 
        if (itemReadOnlyBools.count > 0) { 
         GDataXMLElement *itemReadOnlyElement = (GDataXMLElement *) [itemReadOnlyBools objectAtIndex:0]; 
         NSString *itemReadOnly = itemReadOnlyElement.stringValue; 
         // NSLog(@"itemReadOnly: %@", itemReadOnly); 
         newItem.itemReadOnly = [NSNumber numberWithBool:[itemReadOnly isEqualToString:@"YES"]]; 
        } else continue; 

        // TODO: check my dates.. not sure if this will hold up in other locales 

        // item creation date 
        NSArray *itemCreatedDates = [item elementsForName:@"dateCreated"]; 
        if (itemCreatedDates.count > 0) { 
         GDataXMLElement *dateCreatedElement = (GDataXMLElement *) [itemCreatedDates objectAtIndex:0]; 
         NSString *dateCreated = dateCreatedElement.stringValue; 
         // NSLog(@"dateCreated: %@", dateCreated); 
         newItem.dateCreated = [self dateFromString:dateCreated]; 
        } else continue; 

        // item modification date 
        NSArray *itemModifiedDates = [item elementsForName:@"dateModified"]; 
        if (itemModifiedDates.count > 0) { 
         GDataXMLElement *dateModifiedElement = (GDataXMLElement *) [itemModifiedDates objectAtIndex:0]; 
         NSString *dateModified = dateModifiedElement.stringValue; 
         // NSLog(@"dateModified: %@", dateModified); 
         newItem.dateModified = [self dateFromString:dateModified]; 
        } else continue; 

        // item completed BOOL 
        NSArray *itemCompletedBools = [item elementsForName:@"itemCompleted"]; 
        if (itemCompletedBools.count > 0) { 
         GDataXMLElement *itemCompletedElement = (GDataXMLElement *) [itemCompletedBools objectAtIndex:0]; 
         NSString *itemCompleted = itemCompletedElement.stringValue; 
         // NSLog(@"itemCompleted: %@", itemCompleted); 
         newItem.itemCompleted = [NSNumber numberWithBool:[itemCompleted isEqualToString:@"YES"]]; 
        } else continue; 

        // item completed date 
        NSArray *itemCompletedDates = [item elementsForName:@"dateCompleted"]; 
        if (itemCompletedDates.count > 0) { 
         GDataXMLElement *dateCompletedElement = (GDataXMLElement *) [itemCompletedDates objectAtIndex:0]; 
         NSString *dateCompleted = dateCompletedElement.stringValue; 
         // NSLog(@"dateCompleted string: %@", dateCompleted); 
         newItem.dateCompleted = [self dateFromString:dateCompleted]; 
         // NSLog(@"dateCompleted: %@", newItem.dateCompleted); 
        } else continue; 


        // display order 
        newItem.displayOrder = [NSNumber numberWithInt:counter]; 
        counter++; 


        // assign the new item to the listToUpdate 
        newItem.list = listToUpdate; 
       } 
      } 



      // the list is now imported, so set isUpdating back to NO 
      listToUpdate.isUpdating = [NSNumber numberWithBool:NO]; 



      // Save the context. 
      NSError *saveError = nil; 
      if (![self.importContext save:&saveError]) { 
       NSLog(@"Unresolved error %@, %@", saveError, [saveError userInfo]); 
       abort(); 
      } 
      else { 
       NSLog(@"saved after UPDATING a list while syncing!"); 
      } 


     } 
     else { 
      NSLog(@"UpdateOperation - couldn't find an old version of the list to update!: %@", listName); 
     } 
    } 

    [doc release]; 
    [xmlData release]; 
} 
: Eğer yardımı olacaksa

, burada (ı XML verilerini ayrıştırma listeyi Çekirdek Verilerden eski liste öğeleri silin ve güncellemek) benim NSOperation alt sınıfının "ana" işlevinden kod

Her türlü tavsiye için teşekkür ederiz.

cevap

30

Hata iletisinde, listelenen NSFaultingMutableSet sınıfını görebileceğiniz bir ipucu var. Gerçekten de numaralandırmakta olduğunuz set, potansiyel olarak talep üzerine veri yükleyebilecek çok ilişki için sadece bir vekildir. Koleksiyondaki öğeler numaralandırma sırasında silinmiş olarak işaretlendiğinden, koleksiyonun bir kısmı onu sıralarken bunu değiştirecek ve bu hatayı görebileceksiniz.

Bunun üstesinden gelmenin yaygın bir yolu, koleksiyonun bir kopyasını oluşturmak ve kopyayı sıralamaktır. -copy aslında bir kopyasını döndürür ancak genellikle sadece başka faylanma vekil etmediğini Çekirdek veri ile uğraşırken

NSSet *iterItems = [[list.items copy] autorelease]; 
for (ListItemCD *item in iterItems) { ... } 

Ama buldum: o naif yaklaşım sadece olurdu. Bu yüzden onun yerine koleksiyonu bu şekilde kopyalamak için seç: görünür

NSSet *iterItems = [NSSet setWithSet:list.items]; 
for (ListItemCD *item in iterItems) { ... } 
+0

Tekrar hata görmedim test 20 dakika sonra ... düzelttim etmiş. İnsanların yaptığım gibi bir Çekirdek Veri ilişkisinde nesneleri silmek için hızlı numaralandırma kullandıkları kod örneklerini görmüştüm - ama açıkça bunu yapmak güvenli değil. Gösterdiğiniz gibi ilk önce setin bir kopyasını yapmak çok daha iyi. Yardım için teşekkürler. –

İlgili konular