创建新的 NSManagedObject 并将其分配给新的 NSManagedObject *有时*会失败
Posted
技术标签:
【中文标题】创建新的 NSManagedObject 并将其分配给新的 NSManagedObject *有时*会失败【英文标题】:Creating and assigning a new NSManagedObject to a new NSManagedObject *sometimes* fails 【发布时间】:2016-02-11 22:55:42 【问题描述】:我正在创建一个名为“translation”的新 NSManagedObject。在翻译中,我需要创建两个额外的 NSManagedObjects,称为“短语”。有时其中一个短语分配会引发错误,但是当我检查这些值时,它们看起来都像是被创建得很好。什么给了???
创建翻译对象:
func getOrCreateTranslation(package: Package?, data: NSDictionary) -> Translation
let translationId = data["id"] as! NSNumber
if let translation = self.getTranslation(translationId)
return translation
else
let context = LocalDataStorage().context
let translation = NSEntityDescription.insertNewObjectForEntityForName("Translation", inManagedObjectContext: context) as! Translation
translation.id = translationId
let fromPhrase = data["from_phrase"]! as! NSDictionary
let toPhrase = data["to_phrase"]! as! NSDictionary
let pm = PhraseManager()
//*******
// *SOMETIMES* ONE OF THESE LINES FAIL WITH BAD_EXC_ACCESS code=1
translation.fromPhrase = pm.getOrCreatePhrase(fromPhrase)
translation.toPhrase = pm.getOrCreatePhrase(toPhrase)
//******
if package != nil
package!.addTranslationObject(translation)
return translation
创建短语对象:
func getOrCreatePhrase(data: NSDictionary) -> Phrase
// check if phrase exists
let phraseId = data["id"] as! NSNumber
if let phrase = self.getPhrase(phraseId)
return phrase
else
let context = localDataStorage.context
let lm = LanguageManager()
let phrase = NSEntityDescription.insertNewObjectForEntityForName("Phrase", inManagedObjectContext: context) as! Phrase
phrase.id = phraseId
phrase.text = data["text"] as! String
phrase.audioUrl = data["audio_url"] as? String
let code = data["language"]!["language_code"] as! String
phrase.language = lm.getLanguageFromCode(code)
return phrase
调用 API:
func getPackageTranslations(package: Package, completion: ([Translation])-> Void)
let currentLanguage: Language = LanguageManager().getCurrentLanguage()!
let urlString = baseAPIString + "/groups/\(package.id!)/translations/?language_code=\(currentLanguage.code)"
let session = NSURLSession.sharedSession()
let serachUrl = NSURL(string: urlString)
let task = session.dataTaskWithURL(serachUrl!)
(data, response, error) -> Void in
if error != nil
print(error?.localizedDescription)
else
let jsonData: NSDictionary!
do
jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as! NSDictionary
catch _
jsonData = NSDictionary()
let groupTranslationsData = jsonData["group_translations"] as! [NSDictionary]
var translations = [Translation]()
let context = LocalDataStorage().context
for groupTranslation in groupTranslationsData
let translationData = groupTranslation["translation"] as! NSDictionary
let translation = TranslationManager().getOrCreateTranslation(package, data: translationData)
if translation.packages?.containsObject(package) == false
//package.addTranslationObject(translation!)
//translation!.addPackageObject(package)
translations.append(translation)
do
try context.save()
catch
print("There was a problem saving translation ")
dispatch_async(dispatch_get_main_queue(),
completion(translations)
)
task.resume()
CoreData 上下文类:
class LocalDataStorage
let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext!
init()
context = appDelegate.managedObjectContext
【问题讨论】:
你在哪个线程上运行,上下文是如何配置的? 同意。很可能是由于此代码在创建 managedObjectContext 的不同线程上运行造成的。当你这样做时,它有时会起作用,有时会失败。 感谢两位的回复。 @Wain,我在上面的描述中添加了另外两个涉及的功能,以及正在运行的线程的图片。我确实调用了 API,但对象和 context.save 发生在调度之前。 【参考方案1】:当您创建一个 NSManagedObjectContext 时,该上下文应与其交互的并发模式,并且您在与初始化期间指定的并发模式不同的线程上对其执行操作。
NSURLSession.dataTaskWithURL 的完成块是在另一个线程上运行的,所以你必须调度到上下文创建中指定的线程类型才能成功地对其执行任何操作。
如果您的上下文的并发类型是大多数情况下使用的 MainQueueConcurrencyType,您必须在主队列上执行上下文保存方法。
dispatch_async(dispatch_get_main_queue())
do
try context.save()
catch
print("There was a problem saving translation ")
completion(translations)
【讨论】:
谢谢艾哈迈德!由于调度之前的其他步骤是同步的,我认为它们会在调度之前完全执行。在您的提示之后,我仍然遇到其他保存问题,最终将整个 JSON 解析和对象创建移动到调度块。现在就像一个魅力!以上是关于创建新的 NSManagedObject 并将其分配给新的 NSManagedObject *有时*会失败的主要内容,如果未能解决你的问题,请参考以下文章
RestKit 0.20——创建新的 NSManagedObject 的首选方法是啥?
将现有的 NSManagedObject (s) 导入新的 .xcdatamodeld
为预览目的创建一个假的 NSManagedObjectContext?
创建NSManagedObject子类...在我的项目中创建一个新的错误
为 segue 中传递的新 nsmanagedobject 释放分配的上下文
如何在单个托管对象上下文中初始化新的 NSManagedObject 并设置与另一个 NSManagedObject 的关系?