CoreData的大多數功能依賴於你建立的用於描述實體、實體屬性以及實體間關係的Schema。Schema被表述爲一個被管理對象模型(NSManagedObjectModel)實例對象。一般,這個實例對象越豐富,CoreData對你應用程序的支持越好。html
被管理對象模型有以下特色:ios
NSManagedObjectModel
類的實例。詳情描述以下。git
一個模型包含NSEntityDescription
對象,該對象用於描述一個實體的名稱,以及在運行時被使用來表達這個類的實體的名稱。一個NSEntityDescription
對象還可能包含NSAttributeDescription
以及NSRelationshipDescription
兩個對象。數據實體可能還包含由NSFetchedPropertyDescription
類的實例來表達的可提取屬性。模型還可能有提取請求模板,由NSFetchRequest
的實例來表達。sql
注意:編程
- 在一個模型當中,實體可能被排列在一個繼承結構當中,實體也多是抽象的。
實體繼承swift
對象實體間的繼承與類對象的繼承相似,都是用於將多個實體相同的屬性抽象出來放到父實體當中,從而能夠減小重複建立、維護相同屬性的工做。xcode
注意:數據結構
- 若是你在代碼中建立實體繼承關係,你必須使用自頂向下的方向進行建立。也就是說,你不能夠先建立一個子實體,而後給這個子實體指定父實體。可是你能夠建立一個父實體,而後爲這個父實體指定子實體(使用
.setSubentities
方法)。
抽象實體app
能夠指定一個實體爲抽象的,也就是說在應用程序中不會建立這個實體的實例。當在應用中,有大量的類有相同的一些屬性,咱們能夠將這些屬性抽象出來,組成一個抽象實體,而後讓使用這些通用屬性的實體去繼承這個抽象實體。這樣咱們就完成了對於代碼的複用。框架
一個實體的性質通常是指其屬性(Attribute)以及與其餘實體間的關係,包含提取屬性(Fetched Property)。實體的屬性不能以任何沒有參數的NSObject
或NSManagedObject
類成員函數名來命名。以免混淆。
臨時屬性是做爲模型的一部分來定義的,可是臨時屬性不會做爲實體數據的一部分來持久化到數據存儲當中。CoreData追蹤你對臨時屬性進行的更改,因此它們被記錄以便回滾操做。
實體屬性
CoreData原生支持多種屬性類型,如字符串、日期、整型。若是你想要使用原生沒有的類型做爲實體屬性的類型,可使用在Non-Standard Persistent Attributes中描述的幾種方法。
能夠設置屬性爲optional
的,這樣就能夠不爲實體的這個屬性賦值,也就是說,這個屬性是非必須的。
注意:
- SQL中的
NULL
與iOS中的nil
不一樣,當一個實體的屬性值爲NULL
時,其並表明着iOS中的nil
。如,對於數值型來屬性來講,NULL
就是NULL
,而不是0
。
實體關係
CoreData支持「1對N」、「1對1」關係。能夠指定實體間關係是否爲「必填」和「基數」,以及關聯實體間的刪除方式。關於實體間關係的更多細節,能夠參考Relationships and Fetched Properties。
使用NSFetchRequest
類來描述從持久化數據存儲中提取數據的請求。能夠在一個被管理對象模型中預約義數據提取請求,這樣你能夠預約義按需對模型進行檢索的查詢。關於提取模板的更多的細節能夠參見Accessing and Using a Managed Object Model at Runtime。
被管理對象模型實體、屬性、實體關係中的許多元素都有一個關聯的用戶信息字典。你能夠把任何你想要的信息以鍵值對的開工加入到用戶信息字典當中。被置於用戶信息字典中的經常使用的信息包括一個數據實體的版本細節和用於被提取屬性的查詢條件的值。
配置有一個名稱以及一個關聯的實體集。設置項目多是部分重疊的,這是由於一個實體可能在一個或多個配置中出現。可使用.setEntities:forConfiguration
來編程實現建立配置。或者使用Xcode的數據模型工具來進行設置。
注意:
- 一般,當咱們須要將不一樣的實體存到不一樣的數據中時,咱們須要使用配置。
- 一個持久化存儲協調器只能與一個被管理對象模型進行關聯,因此默認與一個給定的協調器進行關聯的全部存儲都得有一樣的實體。
- 出於以上限制,能夠建立一個包含有全部想要使用的實體的被管理對象模型,而後就能夠被這個模型所包含的實體所組成的各個子集來建立各類配置。
- 而後就能夠建立一個協調器來關聯這個模型。這樣,當想要存儲數據的時候,在協調器中的各個配置將會各個不一樣的實體的值保存到不一樣的持久化存儲當中。
- 必定不能夠建立跨多個持久化存儲的實體關係。
咱們可使用Xcode自帶的CoreData模型工具來建立被管理對象,也能夠徹底是使用編程的方式來實現模型的定製。
使用Xcode工具建立的被管理對象模型有不少在運行時不須要的信息。咱們可使用momc [source] [destination]
來編譯被管理對象模型文件(.xcdatamodel),編譯成用於部署的mom
文件。
若是使用Xcode建立一個使用CoreData的非文檔型應用程序,應用程序委託包含了檢索數據模型的代碼。模型的名稱(模型文件的名稱)在運行時是不被強制關聯的,因此,一旦數據模型被CoreData加載後,文件名就無關僅要了且也不會被使用了。因此,可使用任意的名稱來命名模型文件。
在建立項目的時候,若是勾選了Use Core Data
,則在AppDelegate
文件中,將會有以下代碼自動生成:
// MARK: - Core Data stack lazy var applicationDocumentsDirectory: NSURL = { // The directory the application uses to store the Core Data store file. This code uses a directory named "com.46day.CoreDataUsing" in the application's documents Application Support directory. let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) return urls[urls.count-1] as! NSURL }() lazy var managedObjectModel: NSManagedObjectModel = { // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model. let modelURL = NSBundle.mainBundle().URLForResource("CoreDataUsing", withExtension: "momd")! return NSManagedObjectModel(contentsOfURL: modelURL)! }() lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = { // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added 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. // Create the coordinator and store var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("CoreDataUsing.sqlite") var error: NSError? = nil var failureReason = "There was an error creating or loading the application's saved data." if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil, error: &error) == nil { coordinator = nil // Report any error we got. var dict = [String: AnyObject]() dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" dict[NSLocalizedFailureReasonErrorKey] = failureReason dict[NSUnderlyingErrorKey] = error error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) // Replace this with code to handle the error appropriately. // abort() 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. NSLog("Unresolved error \(error), \(error!.userInfo)") abort() } return coordinator }() lazy var managedObjectContext: NSManagedObjectContext? = { // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail. let coordinator = self.persistentStoreCoordinator if coordinator == nil { return nil } var managedObjectContext = NSManagedObjectContext() managedObjectContext.persistentStoreCoordinator = coordinator return managedObjectContext }() // MARK: - Core Data Saving support func saveContext () { if let moc = self.managedObjectContext { var error: NSError? = nil if moc.hasChanges && !moc.save(&error) { // Replace this implementation with code to handle the error appropriately. // abort() 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. NSLog("Unresolved error \(error), \(error!.userInfo)") abort() } } }
一般狀況下,咱們不須要去修改這些自動生成的,用於處理被管理數據模型的操做。簡要說明以下。
applicationDocumentsDirectory
- 標識應用程序存儲數據文件的路徑managedObjectModel
- 在應用程序中使用的被管理對象模型persistentStoreCoordinator
- 在應用程序中使用的持久化存儲協調器managedObjectContext
- 在應用程序中使用的被管理對象上下文saveContext
- 用於CoreData保存持久化數據,咱們能夠在此方法中添加一些錯誤處理的操做若是咱們想要在其餘類當中使用系統自動生成在AppDelegate
當中的被管理對象上下文、被管理對象模型等內容,可使用以下方法來獲取一個當前應用的AppDelegate
的單例對象。
var delegate = UIApplication.sharedApplication().delegate as? AppDelegate
其中.sharedApplication()
方法就是用來獲取當前正在運行的應用程序對象(單例)的。
若是想要本身__手動加載__數據模型文件,可使用下面兩種機制:
init?(contentsOfURL url: NSURL)
實例方法從一個指定的URL中加載。mergedModelFromBundles
類方法來從一個制定的Bundle集合中建立一個數據模型。注意:
- 通常來講,在一個應用程序中,使用一個模型文件就足夠了。可是,仍然能夠經過使用多個URL來加載多個模型文件,而後在使用這些模型實例化協調器前,使用
modelByMergingModels
來合併它們。
當咱們使用mergedModelFromBundles
來手動合併多個數據模型文件時,有是會由於多個文件的版本等問題而產生一些異常錯誤。
reason = "'Can't merge models with two different entities named 'EntityName''";
reason = "The model used to open the store is incompatible with the one used to create the store";
針對以上異常,提出以下的解決的方法:
init?(contentsOfURL url: NSURL)
替代使用mergedModelFromBundles
由於模型用於描述數據存儲的數據結構,因此修改模型的任意一個部分都將修改模式(Schema),從而引發與以前建立的模型的衝突。若是修改了模式,那麼就須要將舊的數據遷移到新的數據存儲當中,參考(Core Data Model Versioning and Data Migration Programming Guide)。
注意:
若是想要修改模型,可是還想要保留打開以前建立的舊版本模型的能力,則_必須保留舊版本模型_。CoreData沒法打開不兼容的數據模型,因此,若是想要修改數據模型,可是還要保證可以打開已經存在的數據存儲,須要作到:
- 設置模型版本
- 在編輯模式以前,給當前模型建立一個新的版本
- 編輯模型的新版本,保持模型的就版本不變。
可使用以下方式在運行時獲得被管理對象模型,以便使用這個模型。
let model = self.managedObjectContext?.persistentStoreCoordinator?.managedObjectModel
let model = managedObject.entity.managedObjectModel
首先,咱們須要取得當前使用的數據模型。而後,建立請求以及請求所使用的查詢字符串。最後,使用setFetchRequestTemplate
方法將請求加入到模型型當中。
let model = self.managedObjectContext?.persistentStoreCoordinator?.managedObjectModel var requestTemplate = NSFetchRequest(entityName: "User") var predicateTemplate = NSPredicate(format: "name like aaron", argumentArray: nil) requestTemplate.predicate = predicateTemplate model?.setFetchRequestTemplate(requestTemplate, forName: "UserNameLikeAaron")
在訪問提取請求模板時,傳入的參數必須包含在請求模板當中所使用到的全部實體的鍵值。若是想要測試NULL
,則須要傳入nil
類型對象。具體參考Predicate Format String Syntax。
let model = self.managedObjectContext?.persistentStoreCoordinator?.managedObjectModel var request = model?.fetchRequestTemplateForName("UserNameLikeAaron") var results = self.managedObjectContext?.executeFetchRequest(request!, error: nil)
注意:
若是請求模板的查詢條件當中,沒有參數的須要,則在訪問請求模板時,必須作到:
- 使用
fetchRequestFromTemplateWithName:substitutionVariables
,而後傳入nil
做爲參數。- 或者,使用
fetchRequestTemplateForName
,而後copy
結果。