序言
在了解CoreData余赢,大家有必要了解對象關系映射(英語稱object Relational Mapping构眯,簡稱ORM)愕难。
ORM
ORM是通過使用描述對象和數(shù)據(jù)庫之間映射的元數(shù)據(jù)早龟,可以實現(xiàn)將對象自動持久化到關系數(shù)據(jù)庫當中惫霸。ORM的存在為了解決面向對象與數(shù)據(jù)庫科恩干存在不匹配的一種技術。
一 初識CoreData
1.CoreData是一種在iOS 3系統(tǒng)中葱弟,也是蘋果自己推出的數(shù)據(jù)存儲框架壹店,采用了一種ORM(對象關系映射)的存儲關系。
CoreData一個比較大的優(yōu)勢在于在使用CoreData過程中不需要我們編寫SQL語句
芝加,也就是將OC對象存儲于數(shù)據(jù)庫硅卢,也可以將數(shù)據(jù)庫數(shù)據(jù)轉為OC對象(數(shù)據(jù)庫數(shù)據(jù)與OC對象相互轉換)。
2.CoreData幾個類
(1)NSManagedObjectContext
托管對象上下文藏杖,數(shù)據(jù)庫的大多數(shù)操作是在這個類操作
(2)NSManagedObjectModel
托管對象模型将塑,其中一個托管對象模型關聯(lián)到一個模型文件,里面存儲著數(shù)據(jù)庫的數(shù)據(jù)結構蝌麸。
(3)NSPersistentStoreCoordinator
持久化存儲協(xié)調器点寥,主要負責協(xié)調上下文玉存儲的區(qū)域的關系。
(4)NSManagedObject
托管對象類来吩,其中CoreData里面的托管對象都會繼承此類敢辩。
三 CoreData基本使用
下面開始講解CoreData的基本使用,里面會涉及到源碼弟疆,內容比較多戚长,希望大家靜下來看完。
使用CoreData方式怠苔,有兩種可能同廉。第一種是項目開始就創(chuàng)建帶有CoreData數(shù)據(jù)庫,還有一種項目已經(jīng)開始了,重新接入CoreData恤溶,下面我們第三部分主要講述這兩種方式的過程乓诽。
1.項目開始就使用CoreData
我們在創(chuàng)建項目的時候,勾選Use Core Data
然后在文件目錄中自動生成一個后綴為.xcdatamodeld
的文件
打開AppDelegate
發(fā)現(xiàn)類中多了以下內容
- AppDelegate.h
@property (readonly, strong) NSPersistentContainer *persistentContainer;
- (void)saveContext;
- AppDelegate.m
@synthesize persistentContainer = _persistentContainer;
- (NSPersistentContainer *)persistentContainer {
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
@synchronized (self) {
if (_persistentContainer == nil) {
_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"CoreDataDemo"];
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
if (error != nil) {
// 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.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
abort();
}
}];
}
}
return _persistentContainer;
}
#pragma mark - Core Data Saving support
- (void)saveContext {
NSManagedObjectContext *context = self.persistentContainer.viewContext;
NSError *error = nil;
if ([context hasChanges] && ![context save:&error]) {
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
abort();
}
}
我們可以點開testCoreData.xcdatamodeld文件咒程,我們可以看到實體和關系鸠天。如下圖
圖表說明
-
Add Entity
添加表 -
Add Attrubte
添加屬性
創(chuàng)建后可以清楚的看到模型文件左側的列表,有三個Entities帐姻、Fetch Requests以及Configurations三個選項稠集。
-
ENTITIES
實體 -
FETCH REQUESTS
請求模版 -
CONFIGURATIONS
配置信息
添加完一個實體后,你會發(fā)現(xiàn)一個實體是對應著三個內容饥瓷,分別是Attributes剥纷、Relationships和Fetched Properties。
-
Attributes
屬性 -
Relationships
關聯(lián)關系 -
Fetched Properties
獲取操作
(3)實體屬性類型
我們來分別簡單解釋類型的意義呢铆,從上往下
-
Undefined
:也就是默認值晦鞋,如果參與編譯會報錯 -
Integer 16
:代表整數(shù),范圍是-32768 ~ 32767 -
Integer 32
:代表整數(shù)棺克,范圍是-2147483648 ~ 2147483647 -
Integer 64
:代表整數(shù)悠垛,范圍是–9223372036854775808 ~ 9223372036854775807,還是很大的娜谊,較少用 -
Double
:代表小數(shù) -
Float
:代表小數(shù) -
String
:代表字符串确买,NSString表示 -
Boolean
:代表布爾值,使用NSNumber表示 -
Date
:代表日期時期 -
Binary Data
:代表二進制纱皆,是用NSData表示 -
Transformable
:代表Objective對象湾趾,要遵守NSCoding協(xié)議
(4)關聯(lián)關系
點擊加號,可以添加關聯(lián)關系派草,在inverse這個屬性代表兩個實體在Relationships設置關聯(lián)關系后之后搀缠,是否可以從一個實體中找到另一個實體,這樣使兩個實體具有雙向的關聯(lián)關系近迁。
(5)Editor Style
大家通過點擊下面紅色按鈕艺普,style按鈕可以看出實體和屬性的關系,以及可以看出實體之間的對應的關系钳踊。
上面是coreData的視圖的基本運用衷敌,自己也是一個不斷摸索的過程,下面講述CoreData的基本操作拓瞪。
三 CoreData基本使用
在講述操作之前缴罗,我們首先講述NSManagedObjectContext
,蘋果推薦使用initWithConcurrencyType
方式創(chuàng)建祭埂,在創(chuàng)建時面氓,指定當前是什么類型的并發(fā)隊列兵钮,參數(shù)也是一個枚舉值。
NSManagedObjectContext枚舉值參數(shù)有三個類型:
-
NSConfinementConcurrencyType
:此類型在iOS9之后被蘋果棄用舌界,所以不建議用這個API掘譬。 -
NSPrivateQueueConcurrencyType
:代表私有并發(fā)隊列的類型,操作也是在子線程中完成的呻拌。 -
NSMainQueueConcurrencyType
:代表主并發(fā)隊列類型葱轩,如果在操作過程中,需要涉及到UI操作藐握,則應該使用這個參數(shù)初始化上下文完成操作靴拱。
創(chuàng)建
NSManagedObjectContext
一共有兩種方法,下面分別介紹
1.下面我們以Student
為模型猾普,創(chuàng)建主隊列并發(fā)類型的NSManagedObjectContext
/** NSManagedObjectContext */
@property(nonatomic, strong)NSManagedObjectContext *context;
- (NSManagedObjectContext *)context {
if (_context == nil) {
// 創(chuàng)建上下文對象袜炕,并發(fā)隊列設置為主隊列
_context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
// 創(chuàng)建托管對象模型,并使用Student.momd路徑當做初始化參數(shù)
// .xcdatamodeld文件 編譯之后變成.momd文件 (.mom文件)
NSURL *modelPath = [[NSBundle mainBundle] URLForResource:@"CoreDataDemo" withExtension:@"momd"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelPath];
// 創(chuàng)建持久化存儲調度器
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
// 創(chuàng)建并關聯(lián)SQLite數(shù)據(jù)庫文件初家,如果已經(jīng)存在則不會重復創(chuàng)建
NSString *dataPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
dataPath = [dataPath stringByAppendingFormat:@"/%@.sqlite",@"Student"];
[coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:dataPath] options:nil error:nil];
// 上下文對象設置屬性為持久化存儲器
_context.persistentStoreCoordinator = coordinator;
}
return _context;
}
- 解釋說明
(1) 將NSManagedObjectContext
聲明為一個變量偎窘,并且使用懶加載的形式,方便使用溜在。
(2) .xcdatamodeld
文件 編譯之后變成.momd
文件陌知,所以生成NSManagedObjectModel
托管對象模型的時候,文件后綴名一定要寫對炕泳。
(3) 創(chuàng)建并關聯(lián)SQLite數(shù)據(jù)庫文件纵诞,如果已經(jīng)存在則不會重復創(chuàng)建上祈。
2.使用系統(tǒng)推薦的方法創(chuàng)建
@property (readonly, strong) NSPersistentContainer *persistentContainer;
@synthesize persistentContainer = _persistentContainer;
- (NSPersistentContainer *)persistentContainer {
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
@synchronized (self) {
if (_persistentContainer == nil) {
_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"CoreDataDemo"];
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
if (error != nil) {
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
abort();
}
}];
}
}
return _persistentContainer;
}
使用的時候
self.persistentContainer.viewContext
四 實戰(zhàn) - 增培遵,刪,改登刺,查
4.1 插入操作
CoreData通過NSEntityDescription
的insert
進行插入操作籽腕,這樣就會生成并返回一個托管對象,并將這個對象插入到上下文中纸俭。下面以一個Student
為例:
- (Student *)insert1 {
NSError *error = nil;
// 開始創(chuàng)建托管對象皇耗,并指明好創(chuàng)建的托管對象所屬實體名
Student * student = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:self.context];
//2.根據(jù)表Student中的鍵值,給NSManagedObject對象賦值
student.name = [NSString stringWithFormat:@"Mr-%d",arc4random() % 100];
student.age = arc4random() % 20;
student.sex = arc4random() % 2 == 0 ? @"男" : @"女" ;
student.height = arc4random() % 180;
student.number = arc4random() % 100;
if ([self.context hasChanges] && ![self.context save:&error]) {
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
_resultLbe.text = [NSString stringWithFormat:@"CoreData Error:%@",error.userInfo];
abort();
return nil;
} else {
_resultLbe.text = @"插入數(shù)據(jù)成功";
}
return student;
}
NSManagedObjectContext
將操作的數(shù)據(jù)放到了緩存層中揍很,只有調用了NSManagedObjectContext
的save
后郎楼,才會對數(shù)據(jù)庫進行真正的操作,否則對象僅僅存在內存中窒悔,這樣就很好地避免了數(shù)據(jù)庫的頻繁訪問呜袁。
4.2 刪除操作
CoreData首先通過獲取需要刪除的托管對象,遍歷所需要獲取的對象數(shù)組简珠,逐個刪除阶界,最后調用NSManagedObjectContext
的save
方法。
- (void)delete:(NSArray *)delStudents {
// 獲取數(shù)據(jù)的請求對象,指明對實體進行刪除操作
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
NSMutableArray *delStudentSucces = [NSMutableArray array]; // 保存在數(shù)據(jù)庫中成功被刪除的對象
[delStudents enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
// 通過創(chuàng)建謂詞對象膘融,然后過濾掉符合要求的對象芙粱,也就是要刪除的對象
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"number = %d",obj.number];
request.predicate = predicate;
// 通過執(zhí)行獲取操作,找到要刪除的對象即可
NSError *error = nil;
NSArray<Student *> *students = [self.context executeFetchRequest:request error:&error];
// 開始真正操作氧映,一一遍歷春畔,遍歷符合刪除要求的對象數(shù)組,執(zhí)行刪除操作
[students enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
[self.context deleteObject:obj];
}];
// 錯誤處理
if (error) {
self.resultLbe.text = [NSString stringWithFormat:@"CoreData Error:%@",error.description];
} else {
[delStudentSucces addObject:obj];
}
}];
// 最后保存數(shù)據(jù)岛都,保存上下文拐迁。
if (self.context.hasChanges) {
[self.context save:nil];
}
if (delStudentSucces.count > 0) {
self.resultLbe.text = @"刪除數(shù)據(jù)成功";
}
// 將已經(jīng)在數(shù)據(jù)庫中被刪除的對象從內存中移除
[delStudentSucces enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
[self.dataSources removeObject:obj];
}];
[self.tableView reloadData];
}
4.3 修改操作
將所有數(shù)據(jù)庫中的學生,性別顛倒疗绣,年齡加1操作线召。
- (void)modify:(NSArray *)modifyStudents {
// 獲取數(shù)據(jù)的請求對象,指明對實體進行刪除操作
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
NSMutableArray *modifyStudentSucces = [NSMutableArray array];
[modifyStudents enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
// 通過創(chuàng)建謂詞對象多矮,然后過濾掉符合要求的對象缓淹,也就是要刪除的對象
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"number = %d",obj.number];
request.predicate = predicate;
// 通過執(zhí)行獲取操作,找到要刪除的對象即可
NSError *error = nil;
NSArray<Student *> *students = [self.context executeFetchRequest:request error:&error];
// 開始真正操作塔逃,一一遍歷讯壶,遍歷符合刪除要求的對象數(shù)組,執(zhí)行刪除操作
[students enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
obj.age += 1;
obj.sex = [obj.sex isEqualToString:@"男"] ? @"女" : @"男";
}];
// 錯誤處理
if (error) {
self.resultLbe.text = [NSString stringWithFormat:@"CoreData Error:%@",error.description];
} else {
[modifyStudentSucces addObject:obj];
}
}];
// 最后保存數(shù)據(jù)湾盗,保存上下文伏蚊。
if (self.context.hasChanges) {
[self.context save:nil];
}
if (modifyStudentSucces.count > 0) {
self.resultLbe.text = @"修改數(shù)據(jù)成功";
}
NSArray *news = [self search];
[self.dataSources removeAllObjects];
[self.dataSources addObjectsFromArray:news];
[self.tableView reloadData];
self.resultLbe.text = @"修改數(shù)據(jù)成功";
}
4.4 查找操作
查找操作是是有許多條件限制,根據(jù)條件查找出相應的數(shù)據(jù)格粪,下面以一個例子說明一下(查找出所有的元素躏吊,條件以后細節(jié)會講出)
- (NSArray *)search {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
// 執(zhí)行獲取操作,獲取所有Student托管對象
NSError *error = nil;
NSArray<Student *> *students = [self.context executeFetchRequest:request error:&error];
if (error) {
_resultLbe.text = [NSString stringWithFormat:@"CoreData Error:%@",error.description];
} else {
_resultLbe.text = @"查找數(shù)據(jù)成功";
}
return students;
}
以上就是CoreData的基本使用帐萎,自己也在不斷的完善中比伏,希望上面對大家對CoreData認識會進一步提高。
本文參考 iOS-CoreData詳解與使用 ,非常感謝該作者疆导。
更多同類型文章參考
iOS-SQLite3的使用詳解
iOS-CoreData詳解與使用
iOS-FMDB詳解及使用
iOS SQLite赁项、CoreData、FMDB數(shù)據(jù)庫詳解