【问题标题】:IOS ObjectContext not saving to persistent store despite returning True尽管返回 True,IOS ObjectContext 没有保存到持久存储
【发布时间】:2014-08-30 02:16:39
【问题描述】:

我正在实施斯坦福 CS193p 作业 (PhotoMania) 的 Swift 版本。

我很困惑为什么我的 NSObjectContext 保存命令没有保存到我相信我已经创建的持久存储中。

该应用的目的是有两个标签栏: - 标签栏 #1 包含来自 Flickr 的最近照片。 - 标签栏 #2 包含我最近查看的照片。这意味着每次用户点击 Flickr 照片单元查看图像时,我都应该将该图像的信息存储在持久存储结构中,以便在标签栏 #2 中访问它。

应用的结构如下:

1. AppDelegate 包含存储设置说明:

import UIKit
import CoreData

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    var objectContext:NSManagedObjectContext?


   func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
    self.objectContext = self.createMainQueueManagedObjectContext()
    self.testModel()
    return true
}

func testModel(){
    var photoModel: Photo? = NSEntityDescription.insertNewObjectForEntityForName("Photo", inManagedObjectContext: self.objectContext!) as? Photo
    photoModel!.title = "hi"
    photoModel!.subtitle = "subtitle"
    self.objectContext!.insertObject(photoModel)
    self.objectContext!.save(nil)
    println(self.objectContext!.persistentStoreCoordinator.persistentStores[0].objects)
}


    func createManagedObjectModel()-> NSManagedObjectModel{
        var managedObjectModel:NSManagedObjectModel?
        let modelURL:NSURL = NSBundle.mainBundle().URLForResource("PhotoModel", withExtension: "momd")
        managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL)
        return managedObjectModel!
    }

    func createPersistentStoreCoordinator()-> NSPersistentStoreCoordinator{
        var persistentStoreCoordinator:NSPersistentStoreCoordinator?
        var managedObjectModel:NSManagedObjectModel = self.createManagedObjectModel()
        let storeURL:NSURL = self.applicationDocumentsDirectory().URLByAppendingPathComponent("MOC.sqlite")
        var error:NSError?
        persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
        if (persistentStoreCoordinator?.addPersistentStoreWithType(NSSQLiteStoreType as String, configuration: nil, URL: storeURL, options: nil, error: &error) == nil){
            NSLog("unresolved error %@, %@", error!, error!.userInfo)
            abort()
        }
        return persistentStoreCoordinator!

    }

    func applicationDocumentsDirectory()-> NSURL{
        var hello:NSURL = NSFileManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last as NSURL
        return hello
    }

    func createMainQueueManagedObjectContext()->NSManagedObjectContext{
        var managedObjectContext:NSManagedObjectContext?
        var coordinator:NSPersistentStoreCoordinator = self.createPersistentStoreCoordinator()
        if (coordinator != nil){
            managedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType)
            managedObjectContext!.persistentStoreCoordinator = coordinator
        }
        return managedObjectContext!
    }
}

2.有一个从AppDelegate获取上下文的UITableView子类:

class MyViewController: UITableViewController {


    @IBOutlet weak var refresher: UIRefreshControl?

    let appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate


    var objectContext:NSManagedObjectContext?{
        didSet{
            self.fetchDatabasePhotos()
        }
    }

    var fetchController:NSFetchedResultsController?{
        didSet{
            self.tableView.reloadData()
        }
    }



    override func viewDidLoad() {
         self.objectContext = self.appDelegate.objectContext
        super.viewDidLoad()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func fetchDatabasePhotos(){
        let request:NSFetchRequest = NSFetchRequest(entityName: "Photo")
        request.predicate = nil
        request.sortDescriptors = [NSSortDescriptor(key: "title", ascending: true)]
        self.fetchController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: self.objectContext, sectionNameKeyPath: nil, cacheName: nil)
        println(self.fetchController!)
       // println(self.fetchController!.fetchedObjects)
    }
}

3. MyViewController 有一个“JustPostedFlickr”子类,用于管理 Flickr 下载。 Flickr 下载部分工作正常。但是我尝试通过在表格视图控制器和图像视图控制器之间调用 prepareforsegue 的方法将照片对象插入存储中。这是被调用的方法:

override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
    let indexPath:NSIndexPath = self.tableView.indexPathForCell(sender as UITableViewCell)
    if (segue.identifier == "Display Photo"){
        if (segue.destinationViewController.isKindOfClass(ImageViewController)){
            self.prepareImageViewController(segue.destinationViewController as ImageViewController, photo: self.photos[indexPath.row] as NSDictionary)
        }
    }
}


func prepareImageViewController(ivc:ImageViewController, photo:NSDictionary){
    ivc.imageURL = FlickrFetcher.URLforPhoto(photo, format: FlickrPhotoFormatLarge)
    ivc.title = photo.valueForKeyPath(FLICKR_PHOTO_TITLE) as String
    var photoModel: Photo! = NSEntityDescription.insertNewObjectForEntityForName("Photo", inManagedObjectContext: self.objectContext) as Photo
    photoModel!.title = photo.valueForKeyPath(FLICKR_PHOTO_TITLE) as String
    photoModel!.subtitle = photo.valueForKeyPath(FLICKR_PHOTO_DESCRIPTION) as String
    photoModel!.photoURL = FlickrFetcher.URLforPhoto(photo, format: FlickrPhotoFormatLarge).absoluteString
   photoModel!.created_date = NSDate()
    println(self.objectContext!)
    var theVar:Bool = self.objectContext!.save(nil)
    println(theVar)
    self.fetchDatabasePhotos()
}

self.objectContext!.save(xx) 总是返回真。 self.objectContext.registeredObjects 返回正确的照片模型结构和值 但是,当我获取照片并打印获取的对象列表时,它总是返回 nil。

标签栏#2(最近的照片)的控制器,它应该从 fetchController 获取内容。即使在我点击图像(应该填充存储)之后,fetchcontroller.fetchedObjects 方法每次都返回 nil:

import UIKit

class RecentPhotosController: MyViewController {

    override func viewDidAppear(animated: Bool) {
        self.objectContext = self.appDelegate.createMainQueueManagedObjectContext()
        super.viewDidAppear(animated)
        println("I just appeared")

    }

    override func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
        var sections:Int = 0
        let fetchSections = self.fetchController!.sections
        if (fetchSections != nil){
            sections = fetchSections.count
        }
        return sections
    }

    override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
        var rows:Int = 0
        let fetchRows = self.fetchController!.fetchedObjects
        if (fetchRows != nil){
            rows = fetchRows.count
        }
        return rows

    }

    override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
        let cell = tableView.dequeueReusableCellWithIdentifier("Recent Photo", forIndexPath: indexPath) as UITableViewCell
        let photo:Photo = self.fetchController!.objectAtIndexPath(indexPath)! as Photo
        cell.textLabel.text = photo.title as String
        cell.detailTextLabel.text = photo.subtitle as String
        return cell
    }
}

最后:这是照片模型:

import Foundation
import CoreData

class Photo: NSManagedObject {

    @NSManaged var title: String
    @NSManaged var subtitle: String
    @NSManaged var created_date: NSDate
    @NSManaged var photoURL: String

}

我的问题是:为什么 NSObjectContext 中的对象没有保存到存储中?

最后:我现在正在模拟器上进行测试,但我不认为这是我的问题的根源,因为我实现了 CS193p 的 Photomania 的 Swift 版本(它使用核心数据和持久存储)并且效果很好。

【问题讨论】:

    标签: ios uitableview core-data swift nsmanagedobjectcontext


    【解决方案1】:

    您似乎有两个控制器类,每个都调用self.appDelegate.createMainQueueManagedObjectContext()

    createMainQueueManagedObjectContext 为每个控制器创建一个新的上下文。这很好,但它也调用createPersistentStoreCoordinator,它为每个上下文创建一个新的持久存储协调器。这是一个问题,因为现在您有两个存储协调器,每个都有自己的持久存储,竞相写入磁盘上的同一个文件。谁知道会发生什么取决于每个存储相对于另一个存储读取或写入文件的时间。

    你可能想做的是:

    1. 使用由两个控制器共享的单个托管对象上下文。
    2. 创建单个持久存储协调器,将其提供给每个托管对象上下文,并为每个控制器提供自己的上下文。

    【讨论】:

    • 所以你是正确的,我错误地创建了对象上下文。我现在已经删除了这些调用,以便每个控制器从 appdelegate 调用上下文 - 并且在应用启动时从委托调用 createMainQueueManagedObjectContext。但是对象仍然没有被插入到存储中。
    • 顺便说一下,这里有一个代码示例来展示我如何解决多上下文问题: override func viewDidLoad() { self.objectContext = self.appDelegate.objectContext super.viewDidLoad() }跨度>
    • 我现在可能没有足够的信息来诊断您的问题。逐步完成这些课程,看看你能观察到发生了什么。你怎么知道对象被保存了?第二个控制器何时向它发出获取请求?第二个控制器什么时候刷新它的表格视图?你怎么知道第二个控制器不能使用该对象?您的托管对象上下文现在是如何配置的?
    • 我找到了答案。对我来说,这是一个非常愚蠢的疏忽。希望答案能帮助到我这个级别的其他人。
    • 哦,太好了,抱歉我昨晚通读代码时没听懂。
    【解决方案2】:

    我想通了。它失败的原因是我在定义我的 fetchController 之后没有调用 performfetch 方法。

    let request:NSFetchRequest = NSFetchRequest(entityName: "Photo")
            request.predicate = nil
            request.sortDescriptors = [NSSortDescriptor(key: "title", ascending: true)]
            var fetchController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: self.objectContext, sectionNameKeyPath: nil, cacheName: nil)
            fetchController.performFetch(nil)
    

    此代码(我从斯坦福的 obj-C 代码中汲取灵感)隐藏在课程预交付的 coredata 控制器中。

    如果有人被这个问题难住了,一个可能的解决方案是确保在 UITableView 中定义您的 fetchcontroller 之后或您需要调用数据的任何其他时间调用 performfetch。

    您可以通过以下方式知道错误正在发生:

    1) 通过添加以下脚本在运行模拟器时调试 Sqlite:-com.apple.CoreData.SQLDebug 3 2)(级别 3 允许您检查对象的更新,级别 1 不会)

    3) 您的数据是否被插入,但您仍然看不到它?确保您正在执行提取!

    说实话,我怀疑机器人很多人会因为这个错误被抓 2 天,但如果我们是军团,这应该会有所帮助。

    【讨论】:

      猜你喜欢
      • 2016-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多