我在线程中有第二个MOC,它执行获取请求并更新4个属性。 在主线程中,我注册了NSManagedObjectDidSaveNotification处理程序,它合并了我第二个MOC的更改。 如果我在这个方法中放置一个断点并检查更新的对象,那么我确实看到了新的属性。但是,如果我在一个arrary中添加对象id并且在他们的ID的帮助下发布合并+保存访问相同的对象,那么我将获得旧属性。
令人惊讶的是,我观察到,在2中,一个属性具有新值,而另一个属性没有。这是令人难以置信的,经过几个小时的调试,我无法理解这背后的原因。 如果天才能够对这个问题有所了解,那将会很有帮助。
更新
在我的主要帖子中:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:nil];
- (NSManagedObjectContext *)mainThreadObjectContext
{
if (myMainThreadObjectContext == nil) {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
myMainThreadObjectContext = [[NSManagedObjectContext alloc] init];
[myStateManagedObjectContext setPersistentStoreCoordinator:coordinator];
[myStateManagedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
[myStateManagedObjectContext setRetainsRegisteredObjects:NO];
[myStateManagedObjectContext setUndoManager:nil];
}
}
return myMainThreadObjectContext;
}
我合并了此方法中线程MOC中所做的更改
- (void) contextDidSave:(NSNotification *)notification
{
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:@selector(contextDidSave:) withObject:notification waitUntilDone:NO];
return;
}
NSManagedObjectContext *moc = [notification object];
NSMutableSet *updatedObjects = [NSMutableSet set];
if (![moc isEqual:self.mainThreadManagedObjectContext] && [[moc persistentStoreCoordinator] isEqual:self.persistentStoreCoordinator])
{
@synchronized(self)
{
NSDictionary *userinfoDict = [notification userInfo];
NSSet *insertedObjectSet = [userinfoDict valueForKeyPath:@"inserted.objectID"];
NSSet *updatedObjectSet = [userinfoDict valueForKeyPath:@"updated.objectID"];
[updatedObjects addObjectsFromArray:insertedObjectSet.allObjects];
[updatedObjects addObjectsFromArray:updatedObjectSet.allObjects];
@try {
[self.mainThreadManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}
@catch (NSException * e) {
NSLog(@"Exception %@ when mergning contexts in -[%@ %@]", e, NSStringFromClass(self.class), NSStringFromSelector(_cmd));
}
if(updatedObjects.count>0)
{
[self performSelector:@selector(updatedStatus:) withObject:updatedObjects afterDelay:0.5];
}
NSError *saveError = nil;
if (![self.mainThreadManagedObjectContext save:&saveError])
{
NSLog(@"Could not save objects in -[%@ %@]. Error = %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), saveError);
}
}
}
}
在主线程中,我有从MOC获取现有对象并更新从服务器接收的属性的方法。 但是,在我的辅助线程中,我有方法插入新的托管对象,然后更新在主线程中需要很长时间才能处理的属性
在第二个帖子中,我有以下代码
-(void) startTaskThread
{
thread_ = [[NSThread alloc] initWithTarget:self selector:@selector(executeTask)
object:nil];
[thread_ start];
}
-(void) executeTask
{
@synchronized(self)
{
[self saveContext];
NSLog(@"\n\nStarting Task thread [%@]!!!!\n\n", self);
for(;;)
{
if([[NSThread currentThread] isCancelled])
break;
@autoreleasepool
{
[condition_ lock];
if(taskArray_.count==0)
{
[condition_ wait];
}
[condition_ unlock];
@try {
if(taskArray_.count>0)
{
NSInvocation *invocation = [self.taskArray objectAtIndex:0];
if([[NSThread currentThread] isCancelled])
break;
else if(!([[NSThread currentThread] isCancelled]) && [self
respondsToSelector:invocation.selector ])
{
[invocation invokeWithTarget:self];
[self.taskArray removeObjectAtIndex:0];
}
}
}
@catch (NSException *exception) {
NSLog(@"Exception %@", exception.debugDescription);
}
@finally {
}
}
}
}
// Save Moc.
if([[[self managedObjectContext] insertedObjects] count]>0 || ([[[self
managedObjectContext] updatedObjects] count]>0) )
{
NSError *error = nil;
if(![[self managedObjectContext] save:&error])
{
NSLog(@"Moc Save Error %@", [error localizedDescription]);
}
}
[self.managedObjectContext reset];
}
我的辅助线程中的saveContext方法如下:
-(void) saveContext
{
if(!thread_ || [thread_ isCancelled])
{
// Clean up cache here //
return;
}
_/* --- Here i have code that iterates through the properties that were received from the network and stored in a cache of NSDictionary data type */_
// Now i save the threaded MOC.
if([[[self managedObjectContext] insertedObjects] count]>0 || ([[[self managedObjectContext] updatedObjects] count]>0) )
{
NSError *error = nil;
NSLog(@"We are merging the changes now.. [%d], [%d]", [[[self managedObjectContext] insertedObjects] count], [[[self managedObjectContext] updatedObjects] count]);
if(![[self managedObjectContext] save:&error])
{
NSLog(@"Moc Save Error %@", [error localizedDescription]);
}
}
_// I create a dispatch queue here to repeteadly call this method. This serves as a hear-beat dispatch queue that checks for updated/inserted objects every 5 seconds._
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_queue_t queue = dispatch_queue_create("com.myCompany.mocSaveQ", 0);
dispatch_after(popTime, queue, ^(void)
{
// Save Moc.
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(saveContext)]];
invocation.selector = @selector(saveContext);
invocation.target = self;
[self addTask:invocation];
});
dispatch_release(queue);
}
现在,在调试器中,我尝试在线程/第二个MOC上调用[NSManagedObjectContext save]之前在线程中打印更新的对象,并且我确实看到应用于该受管对象的相应属性的更改。 但是,当我尝试在主线程的contextDidSave:方法中为更新的对象打印通知对象的userInfo字典时,虽然它列出了相同的对象,但只有一个在线程中更新的属性没有新值。这让我很困惑。
更新2
以下是更多有助于更好地理解它的代码。
在我的主要帖子中
数据字典是一个NSDictionary,包含属性和子词典
-(void) processData:(NSDictionary*) data
{
for(NSString *key in data)
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:self.managedObjectContext];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"lookupKey = %@", key];
NSArray *fetchResults = nil;
NSError *error = nil;
@synchronized(@"fetch_request_for_lookupKey")
{
@try
{
fetchResults = [moc executeFetchRequest:fetchRequest error:&error];
}
@catch (NSException *exception)
{
NSLog(@"Exception %@ when executing fetch %@", exception, NSStringFromSelector(_cmd));
}
@finally
{
[fetchRequest release];
}
}
NSManagedObject* myManagedObject = fetchResults.lastObject;
// We get the dictionary here
NSDictionary *dictionary = [taskDictionary objectForKey:lookUpKey];
for(NSString *subKey in dictionary)
{
if([key isEqualToString:@"name"])
{
myManagedObject.name = [dictionary objectForKey:subKey];
}
else if([key isEqualToString:@"phone"]
{ //....Update the value...// }
}
}
}
现在在我的辅助线程类中,我在我的辅助线程中的saveContext方法中有上面列出的数据处理逻辑。如果你看到上面的内容,那么我保存我的主线程MOC,然后在我的线程中执行NSFetchRequest。我还确保在执行保存之后以及在我的辅助线程中执行NSFetchRequest之前,主MOC中没有更新的对象。
答案 0 :(得分:0)
这就是我在文档中找到的内容:
并发
Core Data使用线程(或序列化队列)限制来保护托管对象和托管对象上下文(请参阅“与Core Data并发”)。这样做的结果是上下文假定默认所有者是分配它的线程或队列 - 这由调用其init方法的线程确定。因此,您不应该在一个线程上初始化上下文,然后将其传递给另一个线程。相反,您应该传递对持久性存储协调器的引用,并让接收线程/队列创建从该派生协调器派生的新上下文。如果使用NSOperation,则必须在main(用于串行队列)或start(用于并发队列)中创建上下文。
在OS X v10.7及更高版本和iOS v5.0及更高版本中,当您创建上下文时,您可以指定使用它的并发模式(请参阅initWithConcurrencyType :)。
因此它声明我建议使用此方法来获取MOC对象:
- (id)initWithConcurrencyType:(NSManagedObjectContextConcurrencyType)ct;