iOS-進(jìn)階整理03 - CoreData,實(shí)體關(guān)系尽楔,數(shù)據(jù)遷移

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)取消勾選

屏幕快照 2016-05-20 下午3.32.36.png
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刪除了重新生成

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末开伏,一起剝皮案震驚了整個(gè)濱河市膀跌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌固灵,老刑警劉巖捅伤,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異巫玻,居然都是意外死亡丛忆,警方通過查閱死者的電腦和手機(jī)祠汇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來熄诡,“玉大人可很,你說我怎么就攤上這事』烁。” “怎么了我抠?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)袜茧。 經(jīng)常有香客問我菜拓,道長(zhǎng),這世上最難降的妖魔是什么笛厦? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任纳鼎,我火速辦了婚禮,結(jié)果婚禮上裳凸,老公的妹妹穿的比我還像新娘贱鄙。我一直安慰自己,他們只是感情好登舞,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布贰逾。 她就那樣靜靜地躺著,像睡著了一般菠秒。 火紅的嫁衣襯著肌膚如雪疙剑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天践叠,我揣著相機(jī)與錄音言缤,去河邊找鬼。 笑死禁灼,一個(gè)胖子當(dāng)著我的面吹牛管挟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播弄捕,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼僻孝,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了守谓?” 一聲冷哼從身側(cè)響起穿铆,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎斋荞,沒想到半個(gè)月后荞雏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年凤优,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悦陋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡筑辨,死狀恐怖俺驶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情挖垛,我是刑警寧澤痒钝,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布秉颗,位于F島的核電站痢毒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蚕甥。R本人自食惡果不足惜哪替,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望菇怀。 院中可真熱鬧凭舶,春花似錦、人聲如沸爱沟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呼伸。三九已至身冀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間括享,已是汗流浹背搂根。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留铃辖,地道東北人剩愧。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像娇斩,于是被迫代替她去往敵國(guó)和親仁卷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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