gpt4 book ai didi

ios - 核心数据/NSOperation : crash while enumerating through and deleting objects

转载 作者:可可西里 更新时间:2023-11-01 03:02:45 25 4
gpt4 key购买 nike

我有一个基于核心数据的应用程序,它具有一个对象(列表)到多个对象(列表项)的关系。我正在努力在设备之间同步数据,作为其中的一部分,我在后台线程中从 XML 文件导入列表(通过 NSOperation 子类)。

当我更新现有列表时,我删除了它所有的旧列表项(从特定于该线程的 NSManagedObjectContext 中)并用 XML 文件中的新列表项替换它们...删除是通过枚举处理的该列表的项目:

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

但是,偶尔,我会在枚举期间崩溃:

* 由于未捕获的异常“NSGenericException”而终止应用程序,原因:“* 集合 <_NSFaultingMutableSet: 0x4fcfcb0> 在枚举时发生了变异。

我不确定从哪里开始寻找该问题的原因。在进行枚举时,我不会在代码的任何其他部分修改列表。可以同时有多个线程,因为导入/更新了不同的列表......将上下文保存在另一个线程中会导致问题 - 因为它还会通知主上下文(如果它与枚举同时发生) ?

如果有帮助,下面是我的 NSOperation 子类的“主要”函数的代码(我从核心数据中删除旧列表项,并通过解析 XML 数据更新列表):

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

感谢您的任何建议。

最佳答案

错误消息中有一些线索,您可以在其中看到列出的类 NSFaultingMutableSet。事实上,您正在枚举的集合实际上只是对多关系的代理,可能会按需加载数据。由于集合中的项目在枚举期间被标记为已删除,因此在您枚举时集合中的某些项目可能会“更改”并且您会看到该错误。

处理此问题的常用方法是创建集合的副本并枚举副本。天真的做法就是:

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

但我发现在处理 Core Data 时,-copy 实际上并没有返回副本,而通常只是另一个出错的代理。所以我改为选择以这种方式复制集合:

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

关于ios - 核心数据/NSOperation : crash while enumerating through and deleting objects,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6139989/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com