我错误地在后台线程上创建了一组对象,这些对象创建了一个新的managedObjectContext,其中保存了对象。
这导致我的实时应用程序中出现了大量“核心数据无法解决故障”的崩溃问题。请注意 - 我不是要删除这些对象 - 当我尝试在主线程上的app delegate(main)上下文中再次保存它们时,它们的创建方式存在问题。
令人困惑的是,我看到其他对象,不同类型的对象出现此错误。它们可以与后台线程中创建的对象相关,但它们本身并不是在后台线程中创建的。
我对如何发生这种情况感到困惑。如何在后台线程中创建的对象中获取“核心数据无法解决错误”错误,但是应用程序委托(主要)上下文?
还有什么方法可以回到我的用户的实时应用程序中修复这个错误吗?
让我引用我的另一个问题,通过它我发现了这个错误: "Core Data could not fulfill a fault" for objects that were not deleted
我写了一个新问题,因为我觉得这是一个不同的问题 - 尽管肯定是相关的。
以下是在后台线程中创建对象的代码:
- (void)friendPickerViewControllerDidChooseFriends:(NSArray *)friends {
__ENTERING_METHOD__
if (friends.count > 0) {
[[FacebookHelper sharedManager] friendPickerController].navigationController.navigationBar.userInteractionEnabled = NO;
[self startProgressIndicator];
[self performSelectorInBackground:@selector(importFriends:) withObject:friends];
}
else {
[self dismissModalImportViewControllerAnimated];//releases picker delegates, etc
}
}
#pragma mark -
#pragma mark Import Friend
- (void)importFriends:(NSArray*)friends {
__ENTERING_METHOD__
for (NSDictionary<FBGraphUser> *friend in friends) {
[self importFriend:friend withCompletion:^(void){
CGFloat friendNumber = [friends indexOfObject:friend]+1;
CGFloat friendCount = friends.count;
self.importProgress = friendNumber/friendCount;
}];
}
}
- (void)importFriend:(NSDictionary<FBGraphUser>*)friend withCompletion:( void (^) (void) )completionBlock {
__ENTERING_METHOD__
Person *myNewPerson = [GetObjectArrayHelper createNewPersonMocSaveNew:YES];
myNewPerson.facebookID = friend.id;
myNewPerson.facebookName = friend.name;
NSString *nameFirst = [friend.first_name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *nameLast = [friend.last_name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *imageID = friend.id;
UIImage *pickedImage = nil;
if(imageID){
pickedImage = [FacebookHelper imageForObject:imageID];
}
DLog(@"pickedImage:%@",pickedImage);
if(pickedImage){
[self setImagesForFacebookImage:pickedImage forPerson:myNewPerson];
}
//we should ALWAYS have a name
[Helper changePerson:myNewPerson firstName:nameFirst lastName:nameLast];
if(completionBlock) {
completionBlock();
}
}
- (void)finishedImporting {
__ENTERING_METHOD__
[SVProgressHUD showSuccessWithStatus:[self completeString]];
[self performSelector:@selector(dismissModalImportViewControllerAnimated) withObject:nil afterDelay:SV_PROGRESS_HUD_SUCCESS_DELAY];
}
- (void)dismissModalImportViewControllerAnimated {
__ENTERING_METHOD__
[Helper mocSave];//THIS SAVES IN THE APP DELEGATE MANAGED OBJECT CONTEXT -
[SVProgressHUD dismiss];
[self dismissViewControllerAnimated:YES completion:^(void){
[[FacebookHelper sharedManager] friendPickerController].delegate = nil;
[[FacebookHelper sharedManager] friendPickerController].navigationController.navigationBar.userInteractionEnabled = YES;
}];
}
请注意,我关注的对象不是此处创建的任何对象(或此处调用的方法),而是稍后与这些对象关联的对象。
为什么他们的“核心数据无法实现故障”崩溃? (我理解为什么在这里或在这里调用的方法中创建的任何对象都会得到它。)
此外 - 一旦我弄清楚为什么会发生这种混乱(并修复导致它的代码),我需要在用户的实时应用程序中修复恶意对象。我也可以对此提出一些建议!
答案 0 :(得分:2)
根据您的问题和您的意见,您是:
NSManagedObjectContext
不是线程安全的事实。结果,你变得奇怪,令人困惑的崩溃。
这正是人们在这种情况下所期望的。在不采取预防措施的情况下在多个线程上使用托管对象上下文基本上是崩溃和混淆的一个方法。这些崩溃可能没有多大意义,因为你破坏了上下文的内部状态。
如果要在多个线程上使用上下文,必须使用其中一个队列限制选项(NSMainQueueConcurrencyType
或NSPrivateQueueConcurrencyType
),然后 >必须将所有使用上下文的代码或从performBlock:
或performBlockAndWait:
中提取的任何对象放入。 (例外:如果您使用NSMainQueueConcurrencyType
并且您知道您的代码在主线程上,则可以直接使用上下文)。您也可以通过像NSLock
之类的东西使用自己的锁定机制,但是对于大多数人来说,线程化已经足够了。
通常最好每个线程使用一个上下文,作为父/子上下文或独立上下文,但上面的方法也有效。
如果您实际上已经设法保存虚假对象,那么唯一真正的办法是获取这些对象并修复它们或删除它们。如何识别这些对象取决于您的数据模型 - 对于“这个对象废话?”没有通用测试。任何检查都取决于您的应用认为正确的内容。