CoreData 筆記

托管對(duì)象上下文 : NSManagedObejctContext

數(shù)據(jù)庫中的數(shù)據(jù)行在剛被取出時(shí)是Array或Hash形式固阁,這種數(shù)據(jù)結(jié)構(gòu)并不直觀备燃,為了便于使用并齐,通常會(huì)進(jìn)行對(duì)象關(guān)系映射客税,即把類模擬為數(shù)據(jù)行的模板更耻,取出數(shù)據(jù)行時(shí)捏膨,將數(shù)據(jù)行轉(zhuǎn)換為該類的對(duì)象号涯。

把數(shù)據(jù)行存進(jìn)去是個(gè)相反的過程链快,向類的對(duì)象寫數(shù)據(jù)域蜗,再把這個(gè)對(duì)象轉(zhuǎn)為數(shù)據(jù)行存入數(shù)據(jù)庫。

一進(jìn)一出的過程并非憑空完成筑累,它們由 NSManagedObejctContext 控制疼阔。

比如我們來存一條數(shù)據(jù):

//Insert
let moc = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext as! NSManagedObejctContext
let student = NSEntityDescription.insertNewObject(forEntityName: "Student" , into: moc) as! Student
student.id = 1
student.name = "Alice"
student.age = 18
do {
    try moc.save()
    print("保存成功")
} catch {
    fatalError("\(error)")
}

托管對(duì)象模型 : NSManagedObjectModel

簡(jiǎn)單地說,它用來描述數(shù)據(jù)庫的結(jié)構(gòu)巫橄、表關(guān)系茵典。
它的實(shí)例化需要.momd文件,這個(gè)文件存在模擬器沙盒中彩倚,具體位置像這樣:/Users/gaoya/Library/Developer/CoreSimulator/Devices/D07B71B5-8508-47F5-8181-6309D819F52B/data/Containers/Bundle/Application/3B9F46F7-74E1-44AB-8DCD-FBEC3C894EDC/test.app/test.momd

我猜這個(gè)文件是在安裝app的時(shí)候帆离,根據(jù)源文件中的.xcdatamodel文件生成的

實(shí)例化 NSManagedObjectModel:

let modelUrl = Bundle.main.url(forResource: "test.momd/test 2",  withExtension: "mom")!
let managedObjectModel = NSManagedObjectModel.init(contentsOf: modelUrl)

持久化存儲(chǔ)協(xié)調(diào)器 : NSPersistentStoreCooedinator

負(fù)責(zé)管理底層存儲(chǔ)文件哥谷。它包括模型和真實(shí)數(shù)據(jù)。
模型參照上面的NSManagedObjectModel猜扮,真實(shí)數(shù)據(jù)的儲(chǔ)存的方式可以有很多種旅赢,sqlite鲜漩、二進(jìn)制集惋、xml刮刑、json等雷绢。無論用哪一種方式儲(chǔ)存沽讹,真實(shí)數(shù)據(jù)都需要與模型的結(jié)構(gòu)保持一致愿汰,持久化存儲(chǔ)協(xié)調(diào)器的工作就在于此。

自定義上下文的數(shù)據(jù)庫:

//獲取momd文件
let modelUrl = Bundle.main.url(forResource: "test.momd/test 2",  withExtension: "mom")!
//使用momd文件實(shí)例化托管對(duì)象模型
let managedObjectModel = NSManagedObjectModel.init(contentsOf: modelUrl)
//使用托管對(duì)象模型實(shí)例化持久化存儲(chǔ)協(xié)調(diào)器 
let coordinator = NSPersistentStoreCoordinator.init(managedObjectModel: managedObjectModel! )
//獲取沙盒的Document路徑
var applicationDocumentsDirectory: URL = {
    let urls = FileManager.default.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask)
    return urls[urls.count - 1]
}()
let url = applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
//為持久化存儲(chǔ)協(xié)調(diào)器添加真實(shí)數(shù)據(jù)
try! coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
//創(chuàng)建一個(gè)上下文
let managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
//為上下文設(shè)置持久化存儲(chǔ)協(xié)調(diào)器
managedObjectContext.persistentStoreCoordinator = coordinator

獲取沙盒的Document路徑就像這樣: /Users/gaoya/Library/Developer/CoreSimulator/Devices/D07B71B5-8508-47F5-8181-6309D819F52B/data/Containers/Data/Application/57E04E49-DF06-49BE-805D-48CD22D02EB1/Documents,sqlite文件并非一定要儲(chǔ)存在這里惹骂,只要能獲取到就行。


實(shí)體描述 : NSEntityDescription

Entity相當(dāng)于數(shù)據(jù)表轻要,NSEntityDescription 代表一張數(shù)據(jù)表椭懊。
查詢表結(jié)構(gòu):

let moc = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
//et 是 NSEntityDescription實(shí)例
for et in (moc.persistentStoreCoordinator?.managedObjectModel.entities)! {
    print("表名:\(et.name!)")
    print("字段:")
    for (k,_) in et.attributesByName {
        print(k)
    }
}

抓取請(qǐng)求 : NSFetchRequest

NSFetchRequest代表將請(qǐng)求的內(nèi)容,也代表優(yōu)化過的sql語句

// Select
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Student")
request.sortDescriptors = [NSSortDescriptor(key: "id", ascending: true)]   // 依 id 由小到大排序
let selectId = "1"
request.predicate = NSPredicate(format: "id = \(selectId )")   //查找條件
do {
    let results =
        try moc.fetch(request) as! [Student]

    for result in results {
        print("查詢結(jié)果:")
        print("\(result.id). \(result.name!)")
        print("年齡: \(result.age)")
    }
} catch {
    fatalError("\(error)")
}

遷移計(jì)劃 : NSEntityMigrationPolicy

描述一次遷移計(jì)劃踪宠,在映射模型MappingModel文件中指定Custom Policy為自定義的繼承自NSEntityMigrationPolicy的類,Custom Policy處填寫[工程名.類名]柳琢,在該類中復(fù)寫以下可選方法绍妨,則遷移時(shí)會(huì)相應(yīng)調(diào)用它們。

  • 當(dāng)遷移將要開始時(shí)
    func beginEntityMapping(mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool

  • 在舊數(shù)據(jù)上構(gòu)建新的實(shí)例時(shí)
    func createDestinationInstancesForSourceInstance(sInstance: NSManagedObject, entityMapping mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool

  • 在舊數(shù)據(jù)上構(gòu)建新的實(shí)例結(jié)束時(shí)
    func endInstanceCreationForEntityMapping(mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool

  • 構(gòu)建新的RelationShips時(shí)
    func createRelationshipsForDestinationInstance(dInstance: NSManagedObject, entityMapping mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool`

  • 構(gòu)建新的RelationShips結(jié)束時(shí)
    func endRelationshipCreationForEntityMapping(mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool

  • 驗(yàn)證,保存數(shù)據(jù)時(shí)
    func performCustomValidationForEntityMapping(mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool

  • 遷移結(jié)束時(shí)
    func endEntityMapping(mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool


數(shù)據(jù)遷移 : NSMigrationManager

當(dāng)iOS App升級(jí)時(shí)柬脸,本地庫可能會(huì)改動(dòng)他去,如果你直接改動(dòng)本地?cái)?shù)據(jù)庫結(jié)構(gòu),編譯倒堕,安裝(覆蓋舊版本)灾测,打開。
不出意外App會(huì)崩掉涩馆,因?yàn)閿?shù)據(jù)庫結(jié)構(gòu)改動(dòng)了行施,而真實(shí)數(shù)據(jù)沒改動(dòng)允坚。
此時(shí),你應(yīng)該遷移你的數(shù)據(jù)庫蛾号,根據(jù)需求稠项,選擇一種遷移方式執(zhí)行。

輕量遷移

當(dāng)你對(duì)本地庫改動(dòng)不大時(shí)鲜结,使用輕量遷移最快展运,符合輕量遷移的修改條件有:

  • 添加或刪除屬性
  • 非可選屬性變?yōu)榭蛇x屬性,反之亦然(但要填寫默認(rèn)值)
  • 重命名實(shí)體精刷,屬性或關(guān)系

本地庫可以創(chuàng)建多個(gè)版本拗胜,選中.xcdatamodel文件 -> Editor -> Add Model Version


創(chuàng)建新版本

填入新版本名(Version name)、它的基礎(chǔ)版本(Based on model)


創(chuàng)建完成

創(chuàng)建完成怒允,選擇使用你剛創(chuàng)建的版本


修改新版本

修改符合輕量遷移的修改條件埂软,比如增加一個(gè)表字段(height)

現(xiàn)在,我們可以直接編譯纫事、覆蓋安裝舊版本的App了勘畔,覆蓋后的App將擁有最新的本地庫版本,舊版本地庫的數(shù)據(jù)將自動(dòng)遷移到新版本丽惶,因?yàn)槟阒辉黾恿艘粋€(gè)表字段炫七,程序可以自動(dòng)識(shí)別如何將數(shù)據(jù)遷移到新版本。

輕量遷移前后的表結(jié)構(gòu)log

=========前=========
表名:
Student
字段:
id
age
name
=========后=========
表名:
Student
字段:
age
height
name
id

重量遷移

當(dāng)App版本不斷更新钾唬,本地庫的修改需求超過輕量遷移的能力范圍時(shí)万哪,使用重量遷移。

比如抡秆,現(xiàn)在需要新建一張Teacher表奕巍,表結(jié)構(gòu)與Student一致(id、age琅轧、name伍绳、height)乍桂,將原來的Student表里age超過18歲的人移到Teacher表,未超過的留在Student表效床。

具體步驟:

新建本地庫版本睹酌,添加Teacher表

新建本地庫版本、添加Teacher表剩檀,字段與Student保持一致憋沿,并切換版本到最新


新建類 V2_To_V3_Policy.swift,并指定為Migration_V2_To_V3 某Mappings的遷移計(jì)劃

指定遷移計(jì)劃的方式沪猴,在Custom Policy屬性后填寫[工程名.類名]

新建的這個(gè)類讓它繼承NSEntityMigrationPolicy辐啄,并復(fù)寫createDestinationInstances方法
遷移時(shí)采章,每一條舊本地庫的數(shù)據(jù)都會(huì)調(diào)用一遍這個(gè)方法,sInstance就是舊數(shù)據(jù)壶辜,
manager可以獲取新舊版本本地庫的上下文

import Foundation
import CoreData
class V2_To_V3_Policy: NSEntityMigrationPolicy {
    
    override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
        if sInstance.entity.name == "Student"
        {
            let id = sInstance.primitiveValue(forKey: "id") as! Int
            let name = sInstance.primitiveValue(forKey: "name") as! String
            let age = sInstance.primitiveValue(forKey: "age") as! Int
            let height = sInstance.primitiveValue(forKey: "height") as! Int
            
            if age > 18 {
                let tea = NSEntityDescription.insertNewObject(forEntityName: "Teacher",
                                                              into: manager.destinationContext)
                tea.setValue(id, forKey: "id")
                tea.setValue(name, forKey: "name")
                tea.setValue(age, forKey: "age")
                tea.setValue(height, forKey: "height")
            } else {
                let stu = NSEntityDescription.insertNewObject(forEntityName: "Student",
                                                              into: manager.destinationContext)
                stu.setValue(id, forKey: "id")
                stu.setValue(name, forKey: "name")
                stu.setValue(age, forKey: "age")
                stu.setValue(height, forKey: "height")
            }
        }
    }
}

與輕量遷移后一樣悯舟,直接編譯、覆蓋安裝舊版本的App了
啟動(dòng)后砸民,打印一下App的本地庫情況

表名:
    Student
字段:
    age
    height
    name
    id
==================
表名:
    Teacher
字段:
    age
    height
    name
    id
=================
學(xué)生數(shù)據(jù):
    1. Alice1號(hào)
    年齡: 6
    身高:180
    2. Alice2號(hào)
    年齡: 7
    身高:180

    ... ...
    
    13. Alice13號(hào)
    年齡: 18
    身高:180

老師數(shù)據(jù):
    14. Alice14號(hào)
    年齡: 19
    身高:180
    
    15. Alice15號(hào)
    年齡: 20
    身高:180
    
    ... ...
    
    20. Alice20號(hào)
    年齡: 25
    身高:180

漸進(jìn)式遷移

在不考慮輕量遷移的情況下抵怎,App從V1升級(jí)到V2,存在映射模型岭参、指定了遷移計(jì)劃反惕,遷移過程會(huì)很順利地完成,從V2升級(jí)到V3也是如此演侯。但是當(dāng)用戶很久沒更新姿染,直接從V1升級(jí)到V3時(shí),如果沒有專門寫V1到V3的遷移秒际,那么App不出意外會(huì)崩掉悬赏。這就意味著每一次升級(jí),你就需要為每一個(gè)歷史版本提供一份遷移到最新版本的計(jì)劃程癌。
使用漸進(jìn)式遷移舷嗡,每次只遷移到離當(dāng)前最近的一次版本,重復(fù)這個(gè)過程嵌莉,直到成為最新版本进萄。

待補(bǔ)充... ...

Relationship

使用CoreData你就不必再創(chuàng)建xx_id這種表字段了,這種字段可以使用relationship代替锐峭。
在xcdatamodel可視化界面中中鼠,relationships行有3個(gè)參數(shù):relationship/名字、destination/目標(biāo)表名沿癞、inverse/目標(biāo)表的relationships字援雇。
當(dāng)兩個(gè)表各自有一條relationships的destination和inverse指向?qū)Ψ剑徒⒁粭l關(guān)系椎扬,這個(gè)關(guān)系有一些參數(shù)可以選擇惫搏,比如是否級(jí)聯(lián)刪除、關(guān)系類型一對(duì)一或是一對(duì)多等蚕涤。
比如student表和teacher表建立了多對(duì)1的關(guān)系筐赔,那么student的數(shù)據(jù)行映射對(duì)象alice,可以直接使用像"alice.tea.name"獲取老師名字揖铜,其中tea是student表對(duì)teacher表的多對(duì)一關(guān)系名稱茴丰。

結(jié)尾

  • 不要試圖刪除舊版本,要改動(dòng)的話新增版本就行。
  • xcode默認(rèn)幫我們創(chuàng)建了表的同名類贿肩,因?yàn)楸淼腃odgen屬性默認(rèn)為Class Definition峦椰。
  • 如果創(chuàng)建了版本,又使用git了回退汰规,發(fā)現(xiàn)項(xiàng)目編譯不了汤功。可以嘗試在文件管理器中的項(xiàng)目目錄下控轿,找到*.xcdatamodel文件冤竹,右鍵 -> 顯示包內(nèi)容 -> 刪除相關(guān)版本后再編譯。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末茬射,一起剝皮案震驚了整個(gè)濱河市鹦蠕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌在抛,老刑警劉巖钟病,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異刚梭,居然都是意外死亡肠阱,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門朴读,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屹徘,“玉大人,你說我怎么就攤上這事衅金≡胍粒” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵氮唯,是天一觀的道長(zhǎng)鉴吹。 經(jīng)常有香客問我,道長(zhǎng)惩琉,這世上最難降的妖魔是什么豆励? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮瞒渠,結(jié)果婚禮上良蒸,老公的妹妹穿的比我還像新娘。我一直安慰自己伍玖,他們只是感情好诚啃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著私沮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仔燕,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天造垛,我揣著相機(jī)與錄音,去河邊找鬼晰搀。 笑死五辽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的外恕。 我是一名探鬼主播杆逗,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鳞疲!你這毒婦竟也來了罪郊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤尚洽,失蹤者是張志新(化名)和其女友劉穎悔橄,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腺毫,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡癣疟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了潮酒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片睛挚。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖急黎,靈堂內(nèi)的尸體忽然破棺而出扎狱,到底是詐尸還是另有隱情,我是刑警寧澤叁熔,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布委乌,位于F島的核電站,受9級(jí)特大地震影響荣回,放射性物質(zhì)發(fā)生泄漏遭贸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一心软、第九天 我趴在偏房一處隱蔽的房頂上張望壕吹。 院中可真熱鬧,春花似錦删铃、人聲如沸耳贬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咒劲。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間腐魂,已是汗流浹背帐偎。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蛔屹,地道東北人削樊。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像兔毒,于是被迫代替她去往敵國(guó)和親漫贞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 在iOS中框架是一個(gè)目錄育叁,包含了共享資源庫迅脐,用于訪問該資源庫中儲(chǔ)存的代碼的頭文件,以及圖像擂红、聲音文件等其他資源仪际。共...
    nkk閱讀 608評(píng)論 0 1
  • 01树碱、 我媽有個(gè)神奇的能力: 她記得身邊所有人的電話號(hào)碼,包括換煤氣的变秦、收廢紙的成榜、賣掃帚的…… 要換煤氣了,她就說...
    秋落巴士閱讀 292評(píng)論 0 0