Core-Data NSObjectInaccessibleException NSManagedObject 已失效
我有一些代码可以将课程下载为 JSON,解析它们,然后将它们放入核心数据中。然后它们显示在 UITableView 中。目前,当用户有很多课程时,连接有时会超时。因此,我尝试解析传入的课程(使用 SBJson)并将它们一次添加到表格视图中。
两者的代码基本相同,但是当 tableView 内容启动时,新代码会导致崩溃,并出现错误
"Terminating app due to uncaught exception
'NSObjectInaccessibleException', reason: 'The NSManagedObject with ID:0x5ad0570 <x-coredata://A21AC71F-175B-423D-BF7D-C67BEE094460/Lessons/p18> has been invalidated.'"
我想知道这两个代码清单之间可能导致此错误的区别是什么。原始代码在循环中创建每个核心数据对象,但新代码在下载时创建每个核心数据对象。 listViewArray 是用于填充 UITableView 的数组。
我正在使用 SBJsonStreamParser 和 SBJsonStreamParserAdapter 来解析传入的 Json。
我确实有一个工作实现(未显示),基本上每次接收到新对象时都会调用下面的原始代码(运行每个接收到的对象的完整循环)时间)。不过,我想知道是什么原因导致了错误,而不仅仅是让某些东西正常工作。
这是原始的非流代码,在 connectionDidFinishLoading: 中调用
NSMutableArray *tempListArray = [[NSMutableArray alloc] initWithArray:jsonStreamedData];
if (listViewArray)
[listViewArray release];
listViewArray = [[NSMutableArray alloc] init];
if(![tempListArray count]){
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Message" message:@"No active lessons " delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alertView show];
[alertView release];
}
else {
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSError *error = nil;
[appDelegate.managedObjectContext reset];
NSManagedObjectContext *managedObjectContext = appDelegate.managedObjectContext;
for (int i = 0; i < [tempListArray count]; i++) {
NSFetchRequest *checkRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *lessonEntity = [NSEntityDescription entityForName:@"Lessons" inManagedObjectContext:managedObjectContext];
[checkRequest setEntity:lessonEntity];
NSPredicate *langPredicate = [NSPredicate predicateWithFormat:@"(language = %@)", appDelegate.currentLanguage];
NSPredicate *userPredicate = [NSPredicate predicateWithFormat:@"(username = %@)", appDelegate.userName];
NSPredicate *idPredicate = [NSPredicate predicateWithFormat:@"(content_Id = %@)", [[tempListArray objectAtIndex:i] valueForKey:@"id"]];
[checkRequest setPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:langPredicate, userPredicate, idPredicate, nil]]];
NSArray *checkResults = [managedObjectContext executeFetchRequest:checkRequest error:&error];
[checkRequest release];
NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] init];
if ([checkResults count]) {
Lessons *lessonObj = [checkResults objectAtIndex:0];
lessonObj.cards_count = [[tempListArray objectAtIndex:i] valueForKey:@"cards_count"];
lessonObj.mTitle = [[tempListArray objectAtIndex:i] valueForKey:@"title"];
lessonObj.sound_Url = [[tempListArray objectAtIndex:i] valueForKey:@"audio_url"];
lessonObj.mId = [NSNumber numberWithInt:i];
[tempDict setValue:lessonObj forKey:@"lesson"];
[tempDict setValue: [[tempListArray objectAtIndex:i] objectForKey:@"image_url"] forKey:@"image_url"];
[listViewArray addObject:tempDict];
}
else {
Lessons *newLesson = (Lessons *)[NSEntityDescription insertNewObjectForEntityForName:@"Lessons" inManagedObjectContext:appDelegate.managedObjectContext];
newLesson.cards_count = [[tempListArray objectAtIndex:i] valueForKey:@"cards_count"];
newLesson.mTitle = [[tempListArray objectAtIndex:i] valueForKey:@"title"];
newLesson.sound_Url = [[tempListArray objectAtIndex:i] valueForKey:@"audio_url"];
newLesson.content_Id = [[tempListArray objectAtIndex:i] valueForKey:@"id"];
newLesson.username = appDelegate.userName;
newLesson.language = appDelegate.currentLanguage;
newLesson.mId = [NSNumber numberWithInt:i];
[tempDict setValue:newLesson forKey:@"lesson"];
[tempDict setValue: [[tempListArray objectAtIndex:i] objectForKey:@"image_url"] forKey:@"image_url"];
[listViewArray addObject:tempDict];
}
[tempDict release];
tempDict = nil;
}
if (![appDelegate.managedObjectContext save:&error]) {
NSLog(@"Core Data Error - %@", [error localizedDescription]);
}
// NSMutableArray *tempArray = [NSMutableArray arrayWithArray:listViewArray];
// [listViewArray removeAllObjects];
// [listViewArray addObjectsFromArray:[[tempArray reverseObjectEnumerator] allObjects]];
// tempArray = nil;
}
[tempListArray release];
}
[mListsTableView reloadData];
,这是崩溃的代码,在 parser:foundObject: 中调用。循环代码已被删除,因为每次下载新的 Json 对象时都会调用它。
[jsonStreamedData addObject:dict];
if (!listViewArray)
listViewArray = [[NSMutableArray alloc] init];
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSError *error = nil;
[appDelegate.managedObjectContext reset];
NSManagedObjectContext *managedObjectContext = appDelegate.managedObjectContext;
NSFetchRequest *checkRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *lessonEntity = [NSEntityDescription entityForName:@"Lessons" inManagedObjectContext:managedObjectContext];
[checkRequest setEntity:lessonEntity];
NSPredicate *langPredicate = [NSPredicate predicateWithFormat:@"(language = %@)", appDelegate.currentLanguage];
NSPredicate *userPredicate = [NSPredicate predicateWithFormat:@"(username = %@)", appDelegate.userName];
NSPredicate *idPredicate = [NSPredicate predicateWithFormat:@"(content_Id = %@)", [dict valueForKey:@"id"]];
[checkRequest setPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:langPredicate, userPredicate, idPredicate, nil]]];
NSArray *checkResults = [managedObjectContext executeFetchRequest:checkRequest error:&error];
[checkRequest release];
NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] init];
if ([checkResults count]) {
Lessons *lessonObj = [checkResults objectAtIndex:0];
lessonObj.cards_count = [dict valueForKey:@"cards_count"];
lessonObj.mTitle = [dict valueForKey:@"title"];
lessonObj.sound_Url = [dict valueForKey:@"audio_url"];
lessonObj.mId = [NSNumber numberWithInt:jsonStreamedData.count - 1]; // This should be equivalent to i from the loop in the first code
[tempDict setValue:lessonObj forKey:@"lesson"];
[tempDict setValue: [dict objectForKey:@"image_url"] forKey:@"image_url"];
[listViewArray addObject:tempDict];
}
else {
Lessons *newLesson = (Lessons *)[NSEntityDescription insertNewObjectForEntityForName:@"Lessons" inManagedObjectContext:appDelegate.managedObjectContext];
newLesson.cards_count = [dict valueForKey:@"cards_count"];
newLesson.mTitle = [dict valueForKey:@"title"];
newLesson.sound_Url = [dict valueForKey:@"audio_url"];
newLesson.content_Id = [dict valueForKey:@"id"];
newLesson.username = appDelegate.userName;
newLesson.language = appDelegate.currentLanguage;
newLesson.mId = [NSNumber numberWithInt:jsonStreamedData.count - 1];
[tempDict setValue:newLesson forKey:@"lesson"];
[tempDict setValue: [dict objectForKey:@"image_url"] forKey:@"image_url"];
[listViewArray addObject:tempDict];
}
[tempDict release];
tempDict = nil;
if (![appDelegate.managedObjectContext save:&error]) {
ALog(@"Core Data Error - %@", [error localizedDescription]);
}
// NSMutableArray *tempArray = [NSMutableArray arrayWithArray:listViewArray];
// [listViewArray removeAllObjects];
// [listViewArray addObjectsFromArray:[[tempArray reverseObjectEnumerator] allObjects]];
// tempArray = nil;
//[self getListsLocally];
[mListsTableView reloadData];
最后,这是使用第二个清单时崩溃的实际部分,在 tableView:cellForRowAtIndexPath: 顺便说一下,当 row == 1 时崩溃,而不是 row == 0 。出于某种原因,row 0 没问题......它永远不会当然,有机会加载其他行。
titleLabel.text = [[[listViewArray objectAtIndex:indexPath.row] valueForKey:@"lesson"] valueForKey:@"mTitle"]; // CRASH!
labelCards.text = [NSString stringWithFormat:@"%@ Cards", [[[listViewArray objectAtIndex:indexPath.row] valueForKey:@"lesson"] valueForKey:@"cards_count"]];
if([[listViewArray objectAtIndex:indexPath.row] objectForKey:@"userImageObj"] == nil){
mImageView.backgroundColor = [UIColor grayColor];
if ([[listViewArray objectAtIndex:indexPath.row] objectForKey:@"isThreadLaunched"] == nil) {
[NSThread detachNewThreadSelector:@selector(loadImagesInBackground:) toTarget:self withObject:[NSNumber numberWithInt:indexPath.row]];
[[listViewArray objectAtIndex:indexPath.row] setObject:@"Yes" forKey:@"isThreadLaunched"];
}
}else {
mImageView.image = [[listViewArray objectAtIndex:indexPath.row] objectForKey:@"userImageObj"];
}
I have some code that downloads lessons as JSON, parses them, and puts them into core data. They are then displayed in a UITableView. Currently, when a user has many lessons the connection sometimes times out. So I am attempting to parse the lessons as they come in (using SBJson) and add them to the tableview one at a time.
The code for the two is basically the same, but the new code results in crashing when the tableView stuff kicks in, with the error
"Terminating app due to uncaught exception
'NSObjectInaccessibleException', reason: 'The NSManagedObject with ID:0x5ad0570 <x-coredata://A21AC71F-175B-423D-BF7D-C67BEE094460/Lessons/p18> has been invalidated.'"
I would like to know what the difference is between these two code listings that might be causing this error. The original code creates each core data object in a loop, but the new code creates each core data object as it is downloaded. listViewArray is the array that is used to populate the UITableView.
I am using SBJsonStreamParser and SBJsonStreamParserAdapter to parse the Json as it comes in.
I do have a working implementation (not shown), that basically calls the original code below each time a new object is received (running through the full loop of received objects each time). I want to know what is causing the error, though, not just get something working.
This is the original non-streaming code, called in connectionDidFinishLoading:
NSMutableArray *tempListArray = [[NSMutableArray alloc] initWithArray:jsonStreamedData];
if (listViewArray)
[listViewArray release];
listViewArray = [[NSMutableArray alloc] init];
if(![tempListArray count]){
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Message" message:@"No active lessons " delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alertView show];
[alertView release];
}
else {
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSError *error = nil;
[appDelegate.managedObjectContext reset];
NSManagedObjectContext *managedObjectContext = appDelegate.managedObjectContext;
for (int i = 0; i < [tempListArray count]; i++) {
NSFetchRequest *checkRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *lessonEntity = [NSEntityDescription entityForName:@"Lessons" inManagedObjectContext:managedObjectContext];
[checkRequest setEntity:lessonEntity];
NSPredicate *langPredicate = [NSPredicate predicateWithFormat:@"(language = %@)", appDelegate.currentLanguage];
NSPredicate *userPredicate = [NSPredicate predicateWithFormat:@"(username = %@)", appDelegate.userName];
NSPredicate *idPredicate = [NSPredicate predicateWithFormat:@"(content_Id = %@)", [[tempListArray objectAtIndex:i] valueForKey:@"id"]];
[checkRequest setPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:langPredicate, userPredicate, idPredicate, nil]]];
NSArray *checkResults = [managedObjectContext executeFetchRequest:checkRequest error:&error];
[checkRequest release];
NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] init];
if ([checkResults count]) {
Lessons *lessonObj = [checkResults objectAtIndex:0];
lessonObj.cards_count = [[tempListArray objectAtIndex:i] valueForKey:@"cards_count"];
lessonObj.mTitle = [[tempListArray objectAtIndex:i] valueForKey:@"title"];
lessonObj.sound_Url = [[tempListArray objectAtIndex:i] valueForKey:@"audio_url"];
lessonObj.mId = [NSNumber numberWithInt:i];
[tempDict setValue:lessonObj forKey:@"lesson"];
[tempDict setValue: [[tempListArray objectAtIndex:i] objectForKey:@"image_url"] forKey:@"image_url"];
[listViewArray addObject:tempDict];
}
else {
Lessons *newLesson = (Lessons *)[NSEntityDescription insertNewObjectForEntityForName:@"Lessons" inManagedObjectContext:appDelegate.managedObjectContext];
newLesson.cards_count = [[tempListArray objectAtIndex:i] valueForKey:@"cards_count"];
newLesson.mTitle = [[tempListArray objectAtIndex:i] valueForKey:@"title"];
newLesson.sound_Url = [[tempListArray objectAtIndex:i] valueForKey:@"audio_url"];
newLesson.content_Id = [[tempListArray objectAtIndex:i] valueForKey:@"id"];
newLesson.username = appDelegate.userName;
newLesson.language = appDelegate.currentLanguage;
newLesson.mId = [NSNumber numberWithInt:i];
[tempDict setValue:newLesson forKey:@"lesson"];
[tempDict setValue: [[tempListArray objectAtIndex:i] objectForKey:@"image_url"] forKey:@"image_url"];
[listViewArray addObject:tempDict];
}
[tempDict release];
tempDict = nil;
}
if (![appDelegate.managedObjectContext save:&error]) {
NSLog(@"Core Data Error - %@", [error localizedDescription]);
}
// NSMutableArray *tempArray = [NSMutableArray arrayWithArray:listViewArray];
// [listViewArray removeAllObjects];
// [listViewArray addObjectsFromArray:[[tempArray reverseObjectEnumerator] allObjects]];
// tempArray = nil;
}
[tempListArray release];
}
[mListsTableView reloadData];
And here is the code that crashes, called in parser:foundObject: The loop code has been removed, since this is called every time a new Json object is downloaded.
[jsonStreamedData addObject:dict];
if (!listViewArray)
listViewArray = [[NSMutableArray alloc] init];
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSError *error = nil;
[appDelegate.managedObjectContext reset];
NSManagedObjectContext *managedObjectContext = appDelegate.managedObjectContext;
NSFetchRequest *checkRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *lessonEntity = [NSEntityDescription entityForName:@"Lessons" inManagedObjectContext:managedObjectContext];
[checkRequest setEntity:lessonEntity];
NSPredicate *langPredicate = [NSPredicate predicateWithFormat:@"(language = %@)", appDelegate.currentLanguage];
NSPredicate *userPredicate = [NSPredicate predicateWithFormat:@"(username = %@)", appDelegate.userName];
NSPredicate *idPredicate = [NSPredicate predicateWithFormat:@"(content_Id = %@)", [dict valueForKey:@"id"]];
[checkRequest setPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:langPredicate, userPredicate, idPredicate, nil]]];
NSArray *checkResults = [managedObjectContext executeFetchRequest:checkRequest error:&error];
[checkRequest release];
NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] init];
if ([checkResults count]) {
Lessons *lessonObj = [checkResults objectAtIndex:0];
lessonObj.cards_count = [dict valueForKey:@"cards_count"];
lessonObj.mTitle = [dict valueForKey:@"title"];
lessonObj.sound_Url = [dict valueForKey:@"audio_url"];
lessonObj.mId = [NSNumber numberWithInt:jsonStreamedData.count - 1]; // This should be equivalent to i from the loop in the first code
[tempDict setValue:lessonObj forKey:@"lesson"];
[tempDict setValue: [dict objectForKey:@"image_url"] forKey:@"image_url"];
[listViewArray addObject:tempDict];
}
else {
Lessons *newLesson = (Lessons *)[NSEntityDescription insertNewObjectForEntityForName:@"Lessons" inManagedObjectContext:appDelegate.managedObjectContext];
newLesson.cards_count = [dict valueForKey:@"cards_count"];
newLesson.mTitle = [dict valueForKey:@"title"];
newLesson.sound_Url = [dict valueForKey:@"audio_url"];
newLesson.content_Id = [dict valueForKey:@"id"];
newLesson.username = appDelegate.userName;
newLesson.language = appDelegate.currentLanguage;
newLesson.mId = [NSNumber numberWithInt:jsonStreamedData.count - 1];
[tempDict setValue:newLesson forKey:@"lesson"];
[tempDict setValue: [dict objectForKey:@"image_url"] forKey:@"image_url"];
[listViewArray addObject:tempDict];
}
[tempDict release];
tempDict = nil;
if (![appDelegate.managedObjectContext save:&error]) {
ALog(@"Core Data Error - %@", [error localizedDescription]);
}
// NSMutableArray *tempArray = [NSMutableArray arrayWithArray:listViewArray];
// [listViewArray removeAllObjects];
// [listViewArray addObjectsFromArray:[[tempArray reverseObjectEnumerator] allObjects]];
// tempArray = nil;
//[self getListsLocally];
[mListsTableView reloadData];
Finally, here is the actual part that crashes when using the second listing, in tableView:cellForRowAtIndexPath: By the way, it crashes when row == 1, not row == 0. For some reason row 0 is okay... It never has a chance to load the other rows, of course.
titleLabel.text = [[[listViewArray objectAtIndex:indexPath.row] valueForKey:@"lesson"] valueForKey:@"mTitle"]; // CRASH!
labelCards.text = [NSString stringWithFormat:@"%@ Cards", [[[listViewArray objectAtIndex:indexPath.row] valueForKey:@"lesson"] valueForKey:@"cards_count"]];
if([[listViewArray objectAtIndex:indexPath.row] objectForKey:@"userImageObj"] == nil){
mImageView.backgroundColor = [UIColor grayColor];
if ([[listViewArray objectAtIndex:indexPath.row] objectForKey:@"isThreadLaunched"] == nil) {
[NSThread detachNewThreadSelector:@selector(loadImagesInBackground:) toTarget:self withObject:[NSNumber numberWithInt:indexPath.row]];
[[listViewArray objectAtIndex:indexPath.row] setObject:@"Yes" forKey:@"isThreadLaunched"];
}
}else {
mImageView.image = [[listViewArray objectAtIndex:indexPath.row] objectForKey:@"userImageObj"];
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
当您在运行提取之前立即对 ManagedObjectContext 调用
reset
时,最有可能发生对象失效。调用reset
会使内存中的对象失效,但在保存之前不会删除它们。如果无效的托管对象被另一个对象(例如数组)保留,那么它将在保存后以无效的形式持续存在。当您运行提取时,提取会返回无效的对象,当您尝试访问它们的属性之一时,这些对象会导致错误reset
意味着在与撤消管理器一起使用时被调用。它不是通用的“擦除上下文”调用。如果要删除现有对象,则需要获取它们并显式删除它们。您的代码还存在一些其他问题。您可以对
checkRequest
数组调用release
,即使您没有创建它。这可能会导致数组随机消失。同样,listViewArray
似乎是一个类属性,但您永远不会使用访问器形式(例如self.listViewArray
)来确保正确保留。The object invalidation most likely occurs when you call
reset
on the managedObjectContext immediately prior to running a fetch. Callingreset
invalidates objects in memory but does not delete them until a save. If an invalidated managed object is retained by another object such as an array, then it will persist past the save in the invalidated form. When you run the fetch, the fetch returns the invalidated objects which cause the error when you try to access one of their attributesreset
is meant to be called when used with the undo manager. It is not a generic, "wipe the context" call. If you want to delete the existing objects, you need to fetch them and delete them explicitly.Your code also has some other issues. You call
release
on thecheckRequest
array even though you didn't create it. This may cause the array to disappear at random. Likewise thelistViewArray
appears to be a class property but you never use the accessor forms e.g.self.listViewArray
to ensure proper retention.最可能的原因是解析器没有在主线程上运行,并且您在主线程和解析器中使用相同的 ManagedObjectContext - 您不能这样做 - 肯定会导致各种奇怪的行为。
您需要在解析器中创建一个新的 ManagedObjectContext 并将其与主线程的 ManagedObjectContext 的 persistenceStore 关联起来
CoreData 框架对此非常清楚 - 您不能跨线程边界共享 ManagedObjectContext
Most likely cause is that the parser is not running on the main thread and you're using the same ManagedObjectContext across the main thread and the parser - you cannot do that - guaranteed to cause all sorts of strange behaviour.
You need to create a new ManagedObjectContext in the parser and associate that with the persistentStore of the main thread's ManagedObjectContext
The CoreData framework is quite clear on this - you cannot share ManagedObjectContext's across thread boundaries