2011-03-23 82 views
0

我正在写一本字典应用程序,我试图导入从字符串的原始数据,每字一个字符串。除此之外,原始输入字符串包含相应单词所属的部分的名称。在我的数据模型我有Word S和PartOfSpeech一个独立的实体,我要创建的类型PartOfSpeech的一个实体为每个语音独特的部分有可能是在输入的字符串,并建立从Word S上的关系,相关言语之语。该PartOfSpeech实体只有一个属性约,name,和一对许多关系到Wordenter image description here核心数据,在缓存的NSMutableDictionary NSManagedObjects,问题

我的第一个执行卷入独特PartOfSpeech实体的可变数组缓存他们,每次用谓词过滤它的。它有效,但速度很慢。我决定通过在一个NSDictionary中缓存PartsOfSpeech来加快速度,现在当我在导入后尝试并保存数据存储时,出现错误“无法使用自己的存储之外的引用保存对象”。它看起来像是在字典中的问题,但我该如何解决它?

这里是工作的代码: (在sniplets managedObjectContext是伊娃和processStringsInBackground:方法在后台线程使用performSelectorInBackground:withObject:方法运行)

- (void) processStringsInBackground:(NSFetchRequest *)wordStringsReq { 
    NSError *err = NULL;  
    NSFetchRequest *req = [[NSFetchRequest alloc] init]; 
    [req setEntity:[NSEntityDescription entityForName:@"PartOfSpeech" inManagedObjectContext:managedObjectContext]]; 
    err = NULL; 
    NSMutableArray *selectedPartsOfSpeech = [[managedObjectContext executeFetchRequest:req error:&err] mutableCopy]; 
    NSPredicate *p = [NSPredicate predicateWithFormat:@"name like[c] $name"]; 
    // NSPredicate *formNamePredicate = [NSPredicate predicateWithFormat:<#(NSString *)predicateFormat#>] 
... 
    for (int i = 0; i < count; i++){ 
...  
     currentPos = [self uniqueEntityWithName:@"PartOfSpeech" usingMutableArray:selectedPartsOfSpeech predicate:p andDictionary:[NSDictionary dictionaryWithObject:partOfSpeech forKey:@"name"]]; 
... 
    } 
} 

- (NSManagedObject *) uniqueEntityWithName:(NSString *) entityName usingMutableArray:(NSMutableArray *)objects predicate:(NSPredicate *)aPredicate andDictionary:(NSDictionary *) params { 
    NSPredicate *p = [aPredicate predicateWithSubstitutionVariables:params]; 
    NSArray *filteredArray = [objects filteredArrayUsingPredicate:p]; 
    if ([filteredArray count] > 0) { 
     return [filteredArray objectAtIndex:0]; 
    } 
    NSManagedObject *newObject = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:managedObjectContext]; 
    NSArray *dicKeys = [params allKeys]; 
    for (NSString *key in dicKeys) { 
     [newObject willChangeValueForKey:key]; 
     [newObject setPrimitiveValue:[params valueForKey:key] forKey:key]; 
     [newObject didChangeValueForKey:key]; 
    } 
    [objects addObject:newObject]; 
    return newObject; 
} 

这里是一样的,但高速缓存使用NSMutableDictionary,之后未能保存:

- (void) processStringsInBackground:(NSFetchRequest *)wordStringsReq { 
    NSError *err = NULL;  
    [req setEntity:[NSEntityDescription entityForName:@"PartOfSpeech" inManagedObjectContext:managedObjectContext]]; 
    NSArray *selectedPartsOfSpeech = [managedObjectContext executeFetchRequest:req error:&err]; 
    NSMutableDictionary *partsOfSpeechChache = [[NSMutableDictionary alloc] init]; 
    for (PartOfSpeech *pos in selectedPartsOfSpeech) { 
     [partsOfSpeechChache setObject:pos forKey:pos.name]; 
    } 
... 
    for (int i = 0; i < count; i++){ 
...  
     currentPos = [self uniqueEntity:@"PartOfSpeech" withName:partOfSpeech usingDictionary:partsOfSpeechChache]; 
... 
    } 
} 

- (NSManagedObject *)uniqueEntity:(NSString *) entityName withName:(NSString *) name usingDictionary:(NSMutableDictionary *) dic { 
    NSManagedObject *pos = [dic objectForKey:name]; 
    if (pos != nil) { 
     return pos; 
    } 
    NSManagedObject *newPos = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:managedObjectContext]; 
    [newPos willChangeValueForKey:@"name"]; 
    [newPos setPrimitiveValue:name forKey:@"name"]; 
    [newPos didChangeValueForKey:@"name"]; 

    [dic setObject:newPos forKey:name]; 
    return newPos; 
} 

你能帮我找到问题吗?

最好的问候, Timofey。

回答

1

错误是由形成不共享相同的持久性存储managedObjects之间的关系引起的。您可以通过以下方式执行此操作:

  • 使用初始化创建托管对象而不将其插入到上下文中。
  • 从上下文删除管理对象,而在另一个物体保持它例如数组,然后与它建立关系。
  • 一不小心创建了两个核心数据堆栈,使你有两个背景和两家店。
  • 易混配置在多存储上下文。

我没有看到您提供的代码会触发问题的任何部分。

+0

哦,那类的两个变体之间的差异只在于部分我张贴在这里。我也尝试从-processStringsInBackground:方法内的字典中检索PartOfSpeech,但它也不起作用。我认为NSArray和NSDictionary保持引用它们包含的对象的方式可能有一些区别,但是在那里也没有发现任何内容。我也尝试使用NSCache而不是NSMutableDictionary,但再次无济于事。 – Ibolit 2011-03-23 13:28:12

0

事实证明,将NSManagedContext传递给与创建它不同的线程是错误的。相反,应该将NSPersistenceStroreCoordinator传递给另一个线程,并在那里创建一个新的托管对象上下文。为了将更改合并到“主要”上下文中,应该保存另一个线程的上下文,在完成主线程上的保存时接收通知并合并更改(请参阅有关核心数据和并发的Apple文档,给你的链接,因为我在Xcode中读取它)。因此,这里是我对代码所做的,使其工作(仅在修改行发布)的变化:

 
— (void) processStringsInBackground:(NSDictionary *) params { 
    NSFetchRequest *wordStringsReq = [params objectForKey:@"wordStringsReq"]; 
    NSPersistentStoreCoordinator *coordinator = [params objectForKey:@"coordinator"]; 
    NSManagedObjectContext *localContext = [[NSManagedObjectContext alloc] init]; 
    [localContext setPersistentStoreCoordinator:coordinator]; 

(所有的managedObjectContext引用被localContext

和主更换线程,我把这种方法正是如此:

 
....... 
    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:req, @"wordStringsReq", persistentStoreCoordinator, @"coordinator", nil]; //the params i pass to the background method 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"NSManagingContextDidSaveChangesNotification" object:nil]; //register to receive the notification from the save 
    [self performSelectorInBackground:@selector(processStringsInBackground:) withObject:dict];

} - (void) handleNotification:(NSNotification *) notific { NSLog(@"got notification, %@", [notific name]); [managedObjectContext mergeChangesFromContextDidSaveNotification:notific]; }

好运

+0

哎呀,忘了问你关于线程。人们通常会提到它,所以我没有想到。 – TechZen 2011-03-23 19:14:33

+0

那么,我在sniplets之前就已经提到过了);但是,它仍然很奇怪,它与数组一起工作,拒绝使用字典。 – Ibolit 2011-03-23 19:50:48

0

很好的答案,虽然有点过时。精美的文档指出,永远不应该在工作线程中使用主NSManagedObjectContext。相反,使用“主”MOC作为父项,为工作者创建一个单独的NSManagedObjectContext专用,然后替代。以下是有关“并发”页面从核心数据编程指南:

https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Conceptual/CoreData/Concurrency.html

片段(SWIFT)

let jsonArray = … //JSON data to be imported into Core Data 
let moc = … //Our primary context on the main queue 

let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType) 
privateMOC.parentContext = moc 

privateMOC.performBlock { 
    for jsonObject in jsonArray { 
     let mo = … //Managed object that matches the incoming JSON structure 
     //update MO with data from the dictionary 
    } 
    do { 
     try privateMOC.save() 
    } catch { 
     fatalError("Failure to save context: \(error)") 
    } 
}