创建新的 NSManagedObject 并将其分配给新的 NSManagedObject *有时* 会失败

Creating and assigning a new NSManagedObject to a new NSManagedObject *sometimes* fails

我正在创建一个名为 "translation" 的新 NSManagedObject。在翻译中,我需要创建两个名为 "phrase" 的额外 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:

    class LocalDataStorage {
    let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let context: NSManagedObjectContext!

    init() {
        context = appDelegate.managedObjectContext
    }

}

当您使用并发模式创建 NSManagedObjectContext 并在其上进行交互并在与初始化期间指定的并发模式不同的线程上对其执行操作时,会出现此问题。

NSURLSession.dataTaskWithURL 的完成块 运行 在另一个线程上,因此您必须分派到上下文创建中指定的线程类型才能成功执行任何操作。

如果你的context的并发类型是MainQueueConcurrencyType,这是大多数情况下使用的,你必须在主队列上执行context save方法。

dispatch_async(dispatch_get_main_queue()) {
  do {
     try context.save()
  } catch {
     print("There was a problem saving translation ")
  }
  completion(translations)
}