demo在此 https://github.com/DaLiWangCC/MyOpen
一投储、CoreData概述
Core Data是一個(gè)功能強(qiáng)大的層,位于SQLite數(shù)據(jù)庫(kù)之上阔馋,它避免了SQL的復(fù)雜性玛荞,能讓我們以更自然的方式與數(shù)據(jù)庫(kù)進(jìn)行交互。Core Data將數(shù)據(jù)庫(kù)行轉(zhuǎn)換為OC對(duì)象(托管對(duì)象)來實(shí)現(xiàn)呕寝,這樣無需任何SQL知識(shí)就能操作他們勋眯。
但是sql的簡(jiǎn)單使用也不難啊,還是fmdb好用點(diǎn)下梢,可控性更強(qiáng)客蹋。
CoreData與sqlite的比較
sqlite:
1.基于C接口,需要使用SQL語句孽江,代碼繁瑣
2.在處理大量數(shù)據(jù)時(shí)讶坯,表關(guān)系更直觀
3.在OC中不是可視化
CoreData:
1.可視化,有undo/redo能力
2.可以實(shí)現(xiàn)多種文件格式NSSQLiteStoreType岗屏、NSBinaryStoreType辆琅、NSInMemoryStoreType漱办、NSXMLStoreType等
3.蘋果官方API支持,與iOS結(jié)合更緊密
下面說一下CoreData的核心類和結(jié)構(gòu)
名稱
作用
NSManagedObjectModel
對(duì)象模型婉烟,指定所用對(duì)象文件
NSPersistentStoreCoordinator
持久化存儲(chǔ)協(xié)調(diào)器娩井,設(shè)置對(duì)象的存儲(chǔ)方式和數(shù)據(jù)存放位置
NSManagedObjectContext
對(duì)象管理上下文,負(fù)責(zé)數(shù)據(jù)的實(shí)際操作(重要)
NSEntityDescriptor
實(shí)體描述符似袁,描述一個(gè)實(shí)體洞辣,可以用來生成實(shí)體對(duì)應(yīng)的對(duì)象
NSManagedObject
對(duì)象
NSFetchRequest
對(duì)象查詢,相當(dāng)于SQL的Select語句
二昙衅、CoreData的使用
下面開始建使用CoreData的工程
1.建立使用CoreData的工程
勾選Use Core Data選項(xiàng)
把Include Unit Tests和Include UI Tests選項(xiàng)取消勾選
2.進(jìn)入工程新建數(shù)據(jù)庫(kù)表
可以看到Test_CoreData___.xcdatamodeld文件就是我們生產(chǎn)的CoreData數(shù)據(jù)庫(kù)
點(diǎn)擊左下方的Add Entity可以添加新實(shí)體(表)
使用Attributes選項(xiàng)下的+ -號(hào)可以給表添加和刪除字段
在Relationships下面可以添加表之間的關(guān)系
3.生成實(shí)體類
command+N新建類屋彪,選擇Core Data 下的NSManagedObject subclass文件新建,一直點(diǎn)next绒尊,選擇要新建的model畜挥,選擇新建類的實(shí)體。
Use scalar properties for primitive data types選項(xiàng)婴谱,如果勾選蟹但,數(shù)據(jù)庫(kù)中的integer類型就在類中變成int,不勾選谭羔,就變成NSNumber
產(chǎn)生了八個(gè)類华糖,至此基礎(chǔ)工作才完成,進(jìn)入AppDelegate里面可以看到一些新的關(guān)于CoreData的方法
我們現(xiàn)在實(shí)現(xiàn)以學(xué)生信息的表視圖瘟裸,可以添加修改刪除和查詢客叉。
1.我們先寫兩個(gè)方法,分別用來得到Appdelegate對(duì)象和context對(duì)象
//為了得到臨時(shí)數(shù)據(jù)庫(kù)context
//先得到Appdelegate對(duì)象
-(AppDelegate*)appDelegate
{
//整個(gè)應(yīng)用程序的代理
return [UIApplication sharedApplication].delegate;
}
//得到context對(duì)象
-(NSManagedObjectContext*)context
{
return [[self appDelegate]managedObjectContext];//這是在Appdelegate里面系統(tǒng)自動(dòng)添加的發(fā)
}
2.再寫增刪改查的四個(gè)方法
//為數(shù)據(jù)庫(kù)增加數(shù)據(jù)
-(void)insertData
{
//通過實(shí)體描述對(duì)象话告,獲得實(shí)體(相當(dāng)于得到我們要操作的數(shù)據(jù)庫(kù)表)
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Student" inManagedObjectContext:[self context]];
//寫入數(shù)據(jù)
for (int i = 0; i< 10; i++) {
//插入操作
Student *student = [[Student alloc]initWithEntity:entity insertIntoManagedObjectContext:[self context]];
student.name = [NSString stringWithFormat:@"若風(fēng)%d",i];
student.age = 20+i;
student.gender = @"m";
}
//將context中存儲(chǔ)的數(shù)據(jù)同步到真實(shí)的文件中
[[self appDelegate]saveContext];//這個(gè)方法是在AppDelegate中寫好的
}
//查詢操作
-(NSArray*)fetch
{
//構(gòu)造出需要查詢的實(shí)體
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Student" inManagedObjectContext:[self context]];
//初始化查詢工具
NSFetchRequest *req = [[NSFetchRequest alloc]init];
//為查詢工具設(shè)置所需要查詢的實(shí)體
[req setEntity:entity];
//設(shè)置查詢條件兼搏,不加就是查詢?nèi)?
//NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age < 24"];
//[req setPredicate:predicate];
//排序方法
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES];
[req setSortDescriptors:@[sort]];
//執(zhí)行查詢(相當(dāng)于執(zhí)行查詢語句)
NSArray *allDataArray = [[self context]executeFetchRequest:req error:nil];
if (allDataArray&&allDataArray.count) {
return allDataArray;
}
else
{
NSLog(@"no result");
return nil;
}
}
//刪除數(shù)據(jù)
-(void)deleteData
{
//從當(dāng)前數(shù)據(jù)取出要?jiǎng)h除的對(duì)象
Student *delStu = [self.allDataArray lastObject];
//刪除context里的
[[self context]deleteObject:delStu];
//同步
[[self appDelegate]saveContext];
}
//更新數(shù)據(jù)
-(void)updateData
{
//得到要查詢的表
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Student" inManagedObjectContext:[self context]];
//建立查詢的工具類
NSFetchRequest *req = [[NSFetchRequest alloc]init];
[req setEntity:entity];
//謂詞
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name CONTAINS %@",@"若風(fēng)"];
[req setPredicate:predicate];
//結(jié)果
NSArray *array = [[self context]executeFetchRequest:req error:nil];
//遍歷結(jié)果集更改對(duì)象屬性
for (Student *stu in array) {
stu.name = @"傾城";
}
//更新操作需要同步
[[self appDelegate]saveContext];
}
其他的tableView的設(shè)置
#import "RootViewController.h"
#import "AppDelegate.h"http://導(dǎo)入目的為,得到臨時(shí)數(shù)據(jù)庫(kù)(為了應(yīng)用可以和數(shù)據(jù)文件交互)(context)
#import "Student+CoreDataProperties.h"
@interface RootViewController ()<UITableViewDelegate,UITableViewDataSource>
@property (nonatomic,retain)UITableView *myTabelView;
@property (nonatomic,retain)NSMutableArray *allDataArray;//表視圖用的數(shù)組
@end
//添加按鈕
-(void)addData:(UIBarButtonItem*)sender
{
[self insertData];
}
//查詢按鈕
-(void)fetchData:(UIBarButtonItem*)sender
{
self.dataArray = [self fetch];
self.allDataArray = [NSMutableArray arrayWithArray:[self fetch]];
//刷新UI
[_myTabelView reloadData];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = @"CoreData";
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addData:)];
UIBarButtonItem *serBarBtn = [[UIBarButtonItem alloc]initWithTitle:@"查詢" style:UIBarButtonItemStylePlain target:self action:@selector(fetchData:)];
// Do any additional setup after loading the view.
UIBarButtonItem *delBarBtn = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(deleteData)];
UIBarButtonItem *upDateBarBtn = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(updateData)];
self.navigationItem.rightBarButtonItems = @[serBarBtn,delBarBtn,upDateBarBtn];
//添加tableView
_myTabelView = [[UITableView alloc]initWithFrame:self.view.frame style:UITableViewStylePlain];
[self.view addSubview:_myTabelView];
//設(shè)置代理
_myTabelView.delegate = self;
_myTabelView.dataSource = self;
//注冊(cè)cell
[_myTabelView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"CELL"];
}
#pragma mark -- cell
//rows
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _allDataArray.count;
}
//cell in
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [_myTabelView dequeueReusableCellWithIdentifier:@"CELL" forIndexPath:indexPath];
Student *stu = _allDataArray[indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:@"姓名:%@,年齡:%d",[stu name],stu.age];
cell.imageView.image = [UIImage imageNamed:@"bufu.jpg"];
return cell;
}
實(shí)現(xiàn)效果沙郭,上面的按鈕分別是 增 改 刪 查
三佛呻、CoreData實(shí)體之間的關(guān)系,數(shù)據(jù)庫(kù)遷移
1.實(shí)體之間的關(guān)系病线,類似于表之間的外鍵關(guān)系吓著。但是兩個(gè)實(shí)體比不需要一個(gè)相同的字段。
舉個(gè)栗子送挑,一個(gè)Students實(shí)體绑莺,一個(gè)MyClass實(shí)體,給他們添加一個(gè)關(guān)系惕耕,MyClass設(shè)置為一對(duì)多纺裁,Students設(shè)置為一對(duì)一。
這樣赡突,每次在添加了一個(gè)Student數(shù)據(jù)時(shí)对扶,都可以把他添加到一條MyClass數(shù)據(jù)里区赵。
我們拿出一條MyClass數(shù)據(jù)惭缰,也可以從中取出該MyClass里面的所有Student
(1)首先在MyClass的Relationships中添加一條關(guān)系浪南,Relationship項(xiàng)為關(guān)系的名稱,Destination為鏈接的數(shù)據(jù)實(shí)體(表)漱受,type選擇To Many络凿,表示一個(gè)班級(jí)可以有多個(gè)學(xué)生
Delete Rule表示刪除規(guī)則,規(guī)定如果刪除一個(gè)班級(jí)昂羡,班級(jí)里的學(xué)生如何處理絮记。
nullify斬?cái)鄬W(xué)生和班級(jí)的關(guān)系
NoAction 指向空,一般不選
cascade刪除班級(jí)里的學(xué)生
deny 要把班級(jí)里的學(xué)生刪除虐先,才能刪除這個(gè)班級(jí)
(2)然后在Student實(shí)體的RelationShips中添加一個(gè)關(guān)系怨愤,type設(shè)置為To One,一個(gè)學(xué)生只能對(duì)應(yīng)一個(gè)班級(jí)
(3)這樣關(guān)系就建立好了蛹批,我們?cè)偻ㄟ^上面的方法建立實(shí)體的類
會(huì)發(fā)現(xiàn)在MyClass+CoreDataProperties.h中多了幾個(gè)方法和一個(gè)屬性student
#import "MyClass.h"
NS_ASSUME_NONNULL_BEGIN
@interface MyClass (CoreDataProperties)
@property (nullable, nonatomic, retain) NSNumber *class_id;
@property (nullable, nonatomic, retain) NSNumber *stu_count;
//這個(gè)屬性就是該條MyClass記錄里面所有的Student信息
@property (nullable, nonatomic, retain) NSSet<Student *> *student;
@end
@interface MyClass (CoreDataGeneratedAccessors)
//這四個(gè)方法就是給MyClass添加學(xué)生和刪除學(xué)生的方法撰洗。
//當(dāng)學(xué)生記錄建立好后,使用這些方法腐芍,就可以把學(xué)生添加到對(duì)應(yīng)的MyClass記錄里
- (void)addStudentObject:(Student *)value;
- (void)removeStudentObject:(Student *)value;
- (void)addStudent:(NSSet<Student *> *)values;
- (void)removeStudent:(NSSet<Student *> *)values;
@end
同時(shí)差导,在Student+CoreDataProperties.h中也多了一個(gè)屬性myClass
#import "Student.h"
NS_ASSUME_NONNULL_BEGIN
@interface Student (CoreDataProperties)
@property (nullable, nonatomic, retain) NSNumber *age;
@property (nullable, nonatomic, retain) NSString *gender;
@property (nullable, nonatomic, retain) NSString *name;
//表示該學(xué)生所屬的MyClass
@property (nullable, nonatomic, retain) MyClass *myclass;
@end
使用的時(shí)候這樣用
//創(chuàng)建一個(gè)MyClass對(duì)象
MyClass *myClass = [NSEntityDescription insertNewObjectForEntityForName:@"LanOuClass" inManagedObjectContext:[self context]];
//創(chuàng)建一個(gè)Student對(duì)象
Student *stu = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:[self context]];
//將學(xué)生和班級(jí)建立關(guān)聯(lián)
[myClass addStudentObject:stu];
//移除關(guān)聯(lián)
[myClass removeStudentObject:stu];
//得到myClass下的所有student
for (Student *stu in myClass.student) {
//對(duì)stu進(jìn)行操作
}
2.數(shù)據(jù)庫(kù)的遷移
我們知道,當(dāng)一個(gè)項(xiàng)目做得差不多了猪勇,突然需要修改一下數(shù)據(jù)表设褐,比如添加字段,修改字段類型泣刹,改變主外鍵關(guān)系時(shí)助析,會(huì)出無限問題。
如果只是加一個(gè)表椅您,就不用數(shù)據(jù)遷移
在CoreData的實(shí)體需要修改時(shí)貌笨,可以通過數(shù)據(jù)庫(kù)遷移的方式
數(shù)據(jù)遷移有三個(gè)階段
(1)創(chuàng)建基于源實(shí)例對(duì)象的目標(biāo)實(shí)例對(duì)象
(2)重新建立聯(lián)系
(3)驗(yàn)證與保存
a. 按下圖的Creat NSManagedObject Subclass可以新建一個(gè)實(shí)例對(duì)象。然后可以按我們的需求修改里面的表
b. 在下圖右下角Current選項(xiàng)里選擇新建的實(shí)例對(duì)象
c. 在Appdelegate.m的- (NSPersistentStoreCoordinator *)persistentStoreCoordinator 方法里的這句代碼的option參數(shù)里面添加一個(gè)字典
@{NSMigratePersistentStoresAutomaticallyOption:@YES,NSInferMappingModelAutomaticallyOption:@YES}
表示支持版本遷移襟沮,以及版本遷移后自動(dòng)設(shè)置映射關(guān)系
就可以用了锥惋,好累..
//版本遷移要在option加字典
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL
options:@{NSMigratePersistentStoresAutomaticallyOption:@YES,NSInferMappingModelAutomaticallyOption:@YES}
error:&error])
d.如果運(yùn)行還報(bào)錯(cuò),把模擬器的app刪除了重新生成