在后台创建的 NSManagedObject 变成了主线程上的错误
Posted
技术标签:
【中文标题】在后台创建的 NSManagedObject 变成了主线程上的错误【英文标题】:NSManagedObjects created on background turning into faults on main thread 【发布时间】:2017-03-30 11:24:47 【问题描述】:我正在处理一个奇怪的案例。我从 API 获取一些数据,将响应 JSON 转换为 NSManagedObjects 并保存它们,所有这些都在后台线程中并使用父级为 NSMainQueueConcurrencyType
的 NSPrivateQueueConcurrencyType
上下文。
在创建 NSManagedObjects 的同时,我将它们放在一个数组中,以便通过在主线程上执行的完成块在我的视图控制器中使用它。这是我正在做的简化版本。
- (void)getObjectFromAPIWithCompletion:((^)(NSArray *objects, NSError *error))completion
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^
NSManagedObjectContext *backgroundMoc = [[CoreDataStack sharedManager] newChildContextWithConcurrencyType:NSPrivateQueueConcurrencyType];
NSArray *items = [self processToParseResponseFromAPIAndSaveInCoreDataUsingContext:backgroundContext];
[backgroundMoc save:nil];
dispatch_async(dispatch_get_main_queue(), ^
//Beyond this point, all properties of items are nil or 0 (if integer)
if(completion) completion(items, error);
);
);
奇怪的事情发生在完成块内部。我在该块的第一行放置了一个断点并在控制台上打印items
数组。一切看起来都很好,所有项目(在本例中为 50 个)及其属性都正常。
问题是,在那一行之后(我没有更改 items
数组上的任何内容)所有对象都变成错误并且我没有获得任何有关属性的数据。
不知道这种罕见行为的原因是什么。有什么想法吗?
谢谢。
编辑:Hal 的回答让我走上了正轨,所以这就是我为解决这个问题所做的工作。就在调用将在主线程上执行的块之前,我将托管对象从后台上下文“移动”到主上下文,并使用它们的 objectID 获取。像这样:
NSArray *objects = //Objects created on background thread with Private queue
NSMutableArray *objectsIDs = [NSMutableArray array];
for (Object *object in object)
[objectsIDs addObject:object.objectID];
//Save on private managed object context and on completion...
[self.managedObjectContext saveWithCompletion:^(NSError *error)
NSManagedObjectContext *mainMOC = [[CoreDataManager sharedManager] mainContext];
NSMutableArray *fetchedObjects = [NSMutableArray array];
for (NSManagedObjectID *objectID in objectsIDs)
[fetchedArticles addObject:[mainMOC objectWithID:objectID]];
if (completion) completion(fetchedObjects, pagination, nil);
];
这样,所有对象都不是主线程上的故障。
【问题讨论】:
【参考方案1】:如果这些对象(items
的内容)已经在后台线程中创建,则不能在主线程中使用它们。您不能在线程之间传递 NSManagedObject 实例。
您必须传递 NSManagedObjectID(在您保存后台 MOC 之前它不会是永久性的),或者在主线程上使用 NSFetchedResultsController(指向主 MOC)。这是 NSFetchedResultsController 设计的一种情况,所以我建议你走这条路。
【讨论】:
以上是关于在后台创建的 NSManagedObject 变成了主线程上的错误的主要内容,如果未能解决你的问题,请参考以下文章
将 NSManagedObject(在主上下文中创建)从后台线程传递到主线程是不是安全?
Core Data,在后台线程中修改 NSManagedObject