【问题标题】:Core Data NSInvalidArgument Exception核心数据 NSInvalidArgument 异常
【发布时间】:2019-09-30 16:26:50
【问题描述】:

我正在关注这个教程https://medium.com/@jamesrochabrun/parsing-json-response-and-save-it-in-coredata-step-by-step-fb58fc6ce16f

但在到达 fetching 阶段之前,我按照教程的建议运行了应用程序以获取存储数据的 filePath

我的应用程序崩溃并出现错误

'NSInvalidArgumentException', reason: '+entityForName: nil is not a

legal NSManagedObjectContext parameter searching for entity name 
'NewsObject''

我环顾四周,发现了这个iOS: Swift: Core Data: Error: +entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name

Core Data Reading Data

他们都建议确保 manageObjectContext 不为零。但是我不知道如何实现/确保它不是 nil

我是 CoreData 的初学者,刚刚开始学习该教程

这是我的代码


 private func createNewsEntityFrom(dictionary: [String: Any]) -> NSManagedObject? {
        let context = CoreDataStack.sharedInstance.persistentContainer.viewContext
        if let newsEntity = NSEntityDescription.insertNewObject(forEntityName: "NewsObject", into: context) as? NewsObject {
            newsEntity.newsAuthor = dictionary["author"] as? String ?? "default"
            newsEntity.newsTitle = dictionary["title"] as? String ?? "default"
            let images = dictionary["image"] as? [String: AnyObject]
            newsEntity.newsImageURL = images?["link"] as? String ?? "default"
            return newsEntity
        }
        return nil
    }

    private func saveInCoreDataWith(array: [[String: Any]]) {
        for dict in array {
            _ = self.createNewsEntityFrom(dictionary: dict)
        }
        do {
            try CoreDataStack.sharedInstance.persistentContainer.viewContext.save()
        } catch let error {
            print(error)
        }
    }
  let url = "someURL"
            Alamofire.request(url, method: .get , headers: headers).responseJSON { response in
                switch response.result {
                case .success:
                    let json = response.result.value as! [String:Any]
                    let data = json["data"] as! [[String : Any]]
                    self.saveInCoreDataWith(array: data)
                    self.nextToken = json["nextPageToken"] as? String ?? "empty"
                    print("Token = "+self.nextToken!)
                    for dic in data{
                        self.news.append(News(dictionary: dic))
                        print(self.news.count)

                    }
                    DispatchQueue.main.async {
                        loadingIndicator.stopAnimating()
                        self.tableView.reloadData()
                    }


                case .failure: break
                }

我的 CoreDataStack 类



import UIKit
import Foundation
import CoreData

class CoreDataStack: NSObject {

    static let sharedInstance = CoreDataStack()
    private override init() {}

    lazy var persistentContainer: NSPersistentContainer = {
        /*
         The persistent container for the application. This implementation
         creates and returns a container, having loaded the store for the
         application to it. This property is optional since there are legitimate
         error conditions that could cause the creation of the store to fail.
         */
        let container = NSPersistentContainer(name: "TapIn")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                /*
                 Typical reasons for an error here include:
                 * The parent directory does not exist, cannot be created, or disallows writing.
                 * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                 * The device is out of space.
                 * The store could not be migrated to the current model version.
                 Check the error message to determine what the actual problem was.
                 */
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()

    // MARK: - Core Data Saving support

    func saveContext () {
        let context = persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }

}

extension CoreDataStack {

    func applicationDocumentsDirectory() {
        // The directory the application uses to store the Core Data store file. This code uses a directory named "yo.BlogReaderApp" in the application's documents directory.
        if let url = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).last {
            print(url.absoluteString)
        }
    }
}

编辑:这是我用来保存接收到的数据并对其执行各种功能的结构新闻

//Model to hold our news
   struct News {
    var image : String
    var title : String
    var publisherIcon : String
    var publisher : String
    var author : String
    var time : Int
    var id : String
    var bookmarked : Bool
    var liked : Bool

    init(dictionary : [String:Any])
    {
        let image = dictionary["image"] as? [String:Any]
        self.image = image!["link"] as! String
        self.title = dictionary["title"] as? String ?? ""
        self.publisherIcon = dictionary["shortenedLogo"] as? String ?? ""
        self.publisher =  dictionary["publisher"] as? String ?? ""
        self.author = dictionary["author"] as? String ?? ""
        self.time = dictionary["timeToRead"] as? Int ?? 0
        self.id = dictionary["_id"] as? String ?? ""
        self.bookmarked = dictionary["bookmarked"] as? Bool ?? false
        self.liked = dictionary["liked"] as? Bool ?? false
    }

}

在主VCvar news = [News]()

【问题讨论】:

  • 仔细检查您是否已将模型命名为“TapIn”。
  • 模型是指实体吗?
  • 不,模型是您定义实体的位置。因此,您在使用实体(不带扩展名)时选择的 Xcode 中的文件,即您的模型
  • 是的,解决了这个问题,我还实现了 vadian 在下面推荐的更改。谢谢!

标签: ios swift core-data


【解决方案1】:

在单例中添加这个惰性实例化属性以获取非可选上下文

lazy var managedObjectContext : NSManagedObjectContext = {
    return self.persistentContainer.viewContext
}()

并使用现代 API 插入对象

 private func createNewsEntityFrom(dictionary: [String: Any]) -> NewsObject {
    let context = CoreDataStack.sharedInstance.managedObjectContext
    let newsEntity = NewsObject(context: context)
    newsEntity.newsAuthor = dictionary["author"] as? String ?? "default"
    newsEntity.newsTitle = dictionary["title"] as? String ?? "default"
    let images = dictionary["image"] as? [String: Any]
    newsEntity.newsImageURL = images?["link"] as? String ?? "default"
    return newsEntity
 }

根据命名指南,强烈建议将实体和属性命名为较少冗余的示例

 private func createNews(from dictionary: [String: Any]) -> News {
    let context = CoreDataStack.sharedInstance.managedObjectContext
    let news = News(context: context)
    news.author = dictionary["author"] as? String ?? "default"
    news.title = dictionary["title"] as? String ?? "default"
    let images = dictionary["image"] as? [String: Any]
    news.imageURL = images?["link"] as? String ?? "default"
    return news
 }

顺便说一下applicationDocumentsDirectory 函数是错误的。应该是

func applicationDocumentsDirectory() -> URL {
    // The directory the application uses to store the Core Data store file. This code uses a directory named "yo.BlogReaderApp" in the application's documents directory.
    return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last!
}

编辑:

要返回插入的数组,您必须将 saveInCoreDataWith 更改为

private func saveInCoreDataWith(array: [[String: Any]]) -> [NewsObject] {
    var newsArray = [NewsObject]()
    for dict in array {
        newsArray.append(self.createNewsEntityFrom(dictionary: dict))
    }
    do {
        try CoreDataStack.sharedInstance.managedObjectContext.save()
        return newsArray
    } catch let error {
        print(error)
    }
    return []
}

并在 Alamofire 闭包中替换

self.saveInCoreDataWith(array: data)
self.nextToken = json["nextPageToken"] as? String ?? "empty"
print("Token = "+self.nextToken!)
for dic in data{
   self.news.append(News(dictionary: dic))
   print(self.news.count)                   
}

self.news = self.saveInCoreDataWith(array: data)
self.nextToken = json["nextPageToken"] as? String ?? "empty"
print("Token = "+self.nextToken!)
print(self.news.count)

【讨论】:

  • 好的,错误已解决,但是如果我转到打印的目录(按照您所说的对函数进行更改之后)。它是空的。这是否意味着数据没有保存?我在返回之前做了 print(newsEntity.title) 并打印了所有标题
  • 在您的代码中,您插入新对象而不保存上下文。我更新了答案。
  • 感谢您的回复和帮助!关于您在 Alamofire 闭包中建议的更改,它会给我一个错误,因为 news 是我为保存数据而创建的 Struct News 数组,它不能分配给实体 NewsObject 数组。我编辑了问题以详细说明
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多