2014-08-30 87 views
1

我正在實施Stanford CS193p Swift版本(PhotoMania)。IOS ObjectContext不保存到持久性存儲,儘管返回True

我很困惑,爲什麼我的NSObjectContext保存命令沒有保存到持久存儲我相信我已經創建。

該應用程序的目標是有兩個標籤欄: - 標籤欄#1包含來自Flickr的近期照片。 - 標籤欄#2包含我最近瀏覽過的照片。這意味着每次用戶點擊Flickr照片單元以查看圖像時,我都應該將該圖像的信息存儲在持久性存儲結構中,以在標籤欄#2中訪問它。

這裏的應用程序是如何構成的:

的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.有一個UITableView子類,從AppDelegate中獲取上下文:

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。有一個管理Flickr下載的MyViewController的「JustPostedFlickr」子類。 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)總是返回true。 self.objectContext.registeredObjects返回正確的照片模型結構和值 但是,當我獲取照片並打印提取的對象列表時,它始終返回nil。

控制器標籤欄#2(最近的照片),應該從fetchController中獲取內容。該fetchcontroller.fetchedObjects方法返回零每次我在圖像上輕按即使(應該填充存儲):

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版本(它使用核心數據和持久性存儲)並且工作正常。

回答

1

我想通了。它失敗的原因是在定義我的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代碼靈感)被藏在使用過程中的predelivered coredata控制器。

如果有人被這個問題困擾,一個可能的解決方案是確保在UITableView中定義fetchcontroller或任何其他需要調用數據的時間後調用performfetch。

這裏是你如何知道錯誤是發生:

1)調試你的sqlite的同時,加入這個腳本運行模擬器:-com.apple.CoreData.SQLDebug 3 2)(3級允許你檢查對於更新對象,級別1不會)

3)數據是否被插入,但仍然看不到?確保你正在執行取回操作!

說實話,我懷疑bot會在這個錯誤上被抓住2天,但如果我們是軍團,這應該有所幫助。

2

你似乎有兩個控制器類,每個調用self.appDelegate.createMainQueueManagedObjectContext()

createMainQueueManagedObjectContext會爲每個控制器一個新的上下文。這很好,但它也叫createPersistentStoreCoordinator,它爲每個上下文創建一個新的持久存儲協調器。這是一個問題,因爲現在你有兩個商店協調員,每個協調員都有自己的持久存儲區,競相寫入磁盤上的同一個文件。誰知道會發生什麼情況取決於每個商店何時讀取或寫入相對於其他商店的文件。

你可能想要做什麼或者是:

  1. 使用由兩個控制器共享的單一管理對象上下文。
  2. 創建單個持久存儲協調器,將其提供給每個託管對象上下文,併爲每個控制器分配自己的上下文。
+0

所以你是正確的,我錯誤地創建了對象上下文。我現在已經移除了這些調用,以便每個控制器從appdelegate調用上下文 - 並且在應用程序啓動時從代理調用createMainQueueManagedObjectContext。但是這些物品還沒有被插入商店。 – Laurent 2014-08-30 07:48:09

+0

通過這裏的方法是代碼的一個例子來說明我是如何解決的多上下文問題:覆蓋FUNC viewDidLoad中(){ self.objectContext = self.appDelegate.objectContext super.viewDidLoad() } – Laurent 2014-08-30 08:10:57

+0

我大概穿上」現在沒有足夠的信息來診斷您的問題。通過這些課程,看看你可以觀察到的事情發生。你怎麼知道這個對象被保存了?第二個控制器何時對它進行獲取請求?第二個控制器什麼時候刷新它的表格視圖?你怎麼知道對象不可用於第二個控制器?現在如何配置管理對象上下文? – Jonah 2014-08-30 15:39:25