2011-03-23 81 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)") 
    } 
}