我是dispatch_queue的新手,并且在尝试在后台保存到CoreData时遇到了问题。我已经阅读了CoreData编程指南,并且我在后台线程中创建了一个单独的NSManagedObjectContext
。当我在测试项目中创建一个简单的循环来创建NSManagedObject
时,我没有任何问题,创建了对象,并使用NSManageObjectContextDidSaveNotification
将更改传达给主线程。
我相信我的问题在于我对GCD的无知。我正在解析XML并在parserDidEndDocument:
中我需要将数据保存到CoreData而不阻塞UI。每当使用此块时,我的应用程序内存开始不可控制地滚雪球,直到最后我得到Terminated app due to memory pressure
。
注意:我使用AppDelegate的单例来保存我的NSPersistentStoreCoordinator
,而stuffToSave是我的解析器创建的NSMutablearray
。
任何方向都会受到高度赞赏。我一直在打我的头2天!
-(void)parserDidEndDocument:(NSXMLParser *)parser
dispatch_queue_t backgroundQ = dispatch_queue_create("com.example.myapp", NULL);
__block AppDelegate *app= [[UIApplication sharedApplication]delegate];
__block NSMutableArray *array = self.stuffToSave;
dispatch_async(backgroundQ, ^(void){
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
context.persistentStoreCoordinator = [app persistentStoreCoordinator];
HNField *field = [HNField fieldWithField_id:[NSNumber numberWithInt:0] inContext:context];
//initalize array if needed
if (!field.arrayOfPolylines) field.arrayOfPolylines = [[NSMutableArray alloc]init];
//add polyline to array to save in database
for (id obj in array) {
if ([obj isKindOfClass:[HNPolyline class]]) {
HNPolyline *theLine = (HNPolyline *)obj;
[field.arrayOfPolylines addObject:theLine];
}else if ([obj isKindOfClass:[HNParserPoint class]]){
HNPoint *point = [HNPoint createAPointWithContext:context];
HNParserPoint *pPoint = (HNParserPoint *)obj;
point.point_id = pPoint.point_id;
point.lat = pPoint.lat;
point.lng = pPoint.lng;
point.yield = pPoint.yield;
point.farm_id = self.farm_id;
point.field_id = self.field_id;
point.inField = field;
//add every point in database
[field addFieldPointsObject:point];
}
}
NSError *error;
[context save:&error];
});
self.stuffToSave = nil;
self.parser = nil;
}
编辑1:
我正在从一个不同的类中听NSManageObjectContextDidSaveNotification
而不是我正在进行解析。在viewDidLoad
我有:
// observe the ParseOperation's save operation with its managed object context
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didSave:)
name:NSManagedObjectContextDidSaveNotification
object:nil];
然后我在Apple的“ThreadedCoreData”示例中使用了以下内容。
-(void)didSave:(NSNotification *)notification{
if (notification.object != [self.app managedObjectContext]) {
NSLog(@"not main context");
[self performSelectorOnMainThread:@selector(updateMainContext:) withObject:notification waitUntilDone:NO];
}else{
NSLog(@"b Thread: %@",[NSThread currentThread]);
NSLog(@"main context");
}
}
// merge changes to main context
- (void)updateMainContext:(NSNotification *)notification {
assert([NSThread isMainThread]);
[[self.app managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
NSLog(@"did save");
}
答案 0 :(得分:1)
也许我无法正确理解,但从我所看到的情况来看,我会避免一些事情。
我可能错了,但我认为问题在于你是在后台队列上启动异步操作,但是你最终使用默认的上下文队列,你保证它是线程安全的,但事实并非如此。因此,当您保存时,发送通知,即触发保存,等等,直到您填满内存。要验证这一点,请尝试在updateMainContext中放置一个断点,并查看它被调用的次数。
如果您正在使用不同的上下文,则应使用NSPrivateQueueConcurrencyType对它们进行初始化,这样您就可以确保所有工作都在单独的线程中完成。所以我会重新考虑这样代替调度异步,在这种情况下你可以用dispatch_async来代替:
NSManagedObjectContext *privateCtx = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[privateCtx performBlock:^() {
// put the code you have in dispatch async...
}];
然后在另一个课程上你应该找到一种方法来监听私人语境,当然你需要参考。如果你不能,我认为可以避免任何来自主线程的东西,如果那个类只是从背景中合并的一个点。
答案 1 :(得分:0)
当我发布我使用Google Maps SDK(1.6.1.6332)并且google maps sdk也使用核心数据时,我没有提及或意识到。我在我的app委托中观察NSManagedObjectContextDidSaveNotification
但未能过滤通知。因此谷歌地图发送了NSManagedObjectContextDidSaveNotification
,我的应用程序试图合并这些更改,这是我的问题的根源。
我不知道这是否是最佳解决方案,但适用于我的目的。由于我在我的app委托中测试通知,我只提到了我的主要MOC,因此我使用它来确保通知不是来自它,然后我测试通知来自感兴趣的类通过测试通知的字符串描述(包含需要合并的感兴趣类的多个实例)。这样做会阻止谷歌地图尝试将其更改与我的模型合并。向@Leonardo道歉,他的意见最终导致答案。
- (void)mergeChanges:(NSNotification *)notification {
NSLog(@"saved changes called");
if (notification.object != self.managedObjectContext) {
NSLog(@"call was not main MOC");
NSString *testingString = notification.description;
if ([testingString rangeOfString:@"HNDemoResponse"].location != NSNotFound) {
[self performSelectorOnMainThread:@selector(updateMainContext:) withObject:notification waitUntilDone:YES];
}else NSLog(@"call was goole maps");
}
}