CoreData實(shí)現(xiàn)基礎(chǔ)的增刪改查其實(shí)非常簡(jiǎn)單,作為實(shí)際開(kāi)發(fā)你可能還需要了解CoreData版本遷移,CoreData實(shí)體對(duì)象之間的關(guān)聯(lián)關(guān)系,Context上下文實(shí)際操作過(guò)程中常見(jiàn)的誤操作,NSManagedObjectID的用途.簡(jiǎn)單歸類于高階,其實(shí)只是一些CoreData常用但易錯(cuò)的知識(shí)點(diǎn).
版本遷移
項(xiàng)目中開(kāi)發(fā)根據(jù)業(yè)務(wù)的發(fā)展,新增不同的表,也會(huì)對(duì)原有的表進(jìn)行擴(kuò)充,修改實(shí)體對(duì)象的同時(shí),我們需要對(duì)Model(.xcdatamodeld文件)進(jìn)行升級(jí),避免用戶在升級(jí)的時(shí)候發(fā)生崩潰.
① 新增版本號(hào):

版本名字根據(jù)個(gè)人喜好和項(xiàng)目來(lái)命名

② 版本號(hào)新增之后在Model下可以看到新增的版本

③ 設(shè)置當(dāng)前版本號(hào)為最新的版本號(hào)

④NSMigratePersistentStoresAutomaticallyOption設(shè)置為true表示CoreData會(huì)試著將低版本的持久化存儲(chǔ)區(qū)遷移到最新版本的模型文件,NSInferMappingModelAutomaticallyOption設(shè)置為true表示CoreData會(huì)試著以最為合理地方式自動(dòng)推斷出源模型文件的實(shí)體中,某個(gè)屬性到底對(duì)應(yīng)于目標(biāo)模型文件實(shí)體中的哪一個(gè)屬性.
// 自動(dòng)升級(jí)選項(xiàng)設(shè)置
let options = [
NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true
]
try persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: self.storeURL as URL, options: options)
關(guān)聯(lián)關(guān)系
CoreData中實(shí)體關(guān)聯(lián)關(guān)系有三種,一對(duì)一,一對(duì)多,多對(duì)多,舉一個(gè)簡(jiǎn)單的例子Accoun和Order之間關(guān)系.一個(gè)賬戶有多個(gè)訂單.

圖中的RelationShip有三個(gè)字段,關(guān)聯(lián)關(guān)系的名稱,對(duì)應(yīng)的Destination目標(biāo)表的名字,Inverse表示反向關(guān)系,如果沒(méi)有就是No Inverse.蘋果關(guān)系建議是為了保證數(shù)據(jù)之間的一致性,最好設(shè)置反向關(guān)系.每個(gè)Order對(duì)應(yīng)單一的用戶.


設(shè)置關(guān)聯(lián)之后還需要設(shè)置對(duì)應(yīng)的刪除規(guī)則(Delete Rule),蘋果默認(rèn)的關(guān)聯(lián)規(guī)則是Nullify:

Deny 關(guān)系的Destination中只要有一個(gè)對(duì)象,就不能刪除,如果賬戶還有訂單,就不能刪除賬戶
Nullify 只在逆向關(guān)系Optional的時(shí)候有效,Accout 刪除之后,所有的Order中的account信息設(shè)置為nil
Cascade 刪除對(duì)象后,刪除destination所有對(duì)象.Account刪除之后,會(huì)刪除所有對(duì)應(yīng)的Order對(duì)象.
NoAction 刪除對(duì)象后,對(duì)Destination不做任何操作,在Destination中有大量對(duì)象的時(shí)候有用.
Context上下文
NSManagedObjectContext是連接對(duì)象和數(shù)據(jù)存儲(chǔ)之間的橋梁,Context有兩種并發(fā)類型,主線程和子線程并發(fā)類型.
public enum NSManagedObjectContextConcurrencyType : UInt {
@available(iOS, introduced: 3.0, deprecated: 9.0, message: "Use another NSManagedObjectContextConcurrencyType")
case confinementConcurrencyType
case privateQueueConcurrencyType
case mainQueueConcurrencyType
}
① 如果Context是nil,會(huì)遇到以下錯(cuò)誤:
+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name ‘Account’’
數(shù)據(jù)操作的時(shí)候盡量保證Context不為空.
② 設(shè)置關(guān)聯(lián)關(guān)系,新建立一個(gè)訂單,設(shè)置Order的關(guān)聯(lián)對(duì)象是Account:
do {
let account:Account = Account.findAccountByName(name: "FlyElephant")!
let privateContext:NSManagedObjectContext = try CoreDataManager.sharedManager.newPrivateQueueContextWithNewPSC()
let order:Order = NSEntityDescription.insertNewObject(forEntityName: "Order", into: privateContext) as! Order
order.orderName = "臺(tái)灣小零食--\(1)"
order.orderNumber = Int32(100)
order.account = account
if privateContext.hasChanges {
do {
print("保存成功")
try privateContext.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
} catch {
print(error)
}
Account查詢:
static func findAccountByName(name:String) -> Account? {
let context:NSManagedObjectContext = CoreDataManager.sharedManager.mainQueueContext
let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Account")
let predicate = NSPredicate.init(format: " accountName = %@", name)
fetchRequest.predicate = predicate
do {
let searchResults = try context.fetch(fetchRequest)
if searchResults.count > 0 {
let account:Account = searchResults[0] as! Account
return account
} else {
return nil
}
} catch {
print(error)
}
return nil
}
但是這個(gè)時(shí)候會(huì)報(bào)錯(cuò):
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Illegal attempt to establish a relationship 'account' between objects in different contexts (source = <Order: 0x600000090ef0> (entity: Order; id: 0x60000022ffa0 <x-coredata:///Order/t0026FC90-22DC-459F-B59F-D0A225505AFE3> ; data: {
account = nil;
orderName = "\U53f0\U6e7e\U5c0f\U96f6\U98df--1";
orderNumber = 100;
}) , destination = <Account: 0x600000090310> (entity: Account; id: 0xd000000000040000 <x-coredata://D472E515-79B3-469F-A842-73AE3A38D9F8/Account/p1> ; data: <fault>))'
Account和Order不在同一個(gè)Context上下文中,所以會(huì)出現(xiàn)錯(cuò)誤,可以在同一個(gè)上下文中進(jìn)行實(shí)體對(duì)象查詢:
do {
let account:Account = Account.findAccountByName(name: "FlyElephant")!
let privateContext:NSManagedObjectContext = try CoreDataManager.sharedManager.newPrivateQueueContextWithNewPSC()
let order:Order = NSEntityDescription.insertNewObject(forEntityName: "Order", into: privateContext) as! Order
order.orderName = "FlyElephant-臺(tái)灣小零食--\(1)"
order.orderNumber = Int32(100)
let accountInContext:Account = privateContext.object(with: account.objectID) as! Account
order.account = accountInContext
if privateContext.hasChanges {
do {
print("保存成功")
try privateContext.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
} catch {
print(error)
}
其中的objectID類型是NSManagedObjectID,并不是字符串類型:
open class NSManagedObjectID : NSObject, NSCopying {
open var entity: NSEntityDescription { get } // entity for the object identified by an ID
weak open var persistentStore: NSPersistentStore? { get } // persistent store that fetched the object identified by an ID
open var isTemporaryID: Bool { get } // indicates whether or not this ID will be replaced later, such as after a save operation (temporary IDs are assigned to newly inserted objects and replaced with permanent IDs when an object is written to a persistent store); most IDs return NO
open func uriRepresentation() -> URL // URI which provides an archivable reference to the object which this ID refers
}`</code></pre>