iOS-CoreData之深入浅出
一.数据持久化
什么是数据持久化,内存中的数据在关机之后就不复存在了,而磁盘上的数据可以持久保存着,应用程序产生的数据和从网络上得到的数据,刚开始是在内存中,如果不把它存储到磁盘上保存,这样的话每次都会去生成和从网络上获得数据,有可能得到的数据是一样的这样就会造成操作的浪费,网络资源的浪费,程序体验不好,我们的一些用户习惯信息或者用户登录的重要数据也可以存储在持久化到本地,说白了就是把内存中的实体对象把它归档到外部的永久存储设备上。应用程序相应的也能够从外表存储设备上取出数据。
二.Managed Object
在CoreData框架中,Managed Object是主角,所有的类都为它服务,它时数据的在内存中的载体,持久化的对象就是把它承载的数据保存到永久存储器中,从永久存储器中取出来的数据也是通过它来承载,供应用程序使用。
a.
for (int i=0; i<5; i++) {
NSObject *temp = [[NSObject alloc]init];
}
b.
NSMutableArray *arr = [[NSMutableArray alloc]init];
for (int i=0; i<5; i++) {
NSObject *temp = [[NSObject alloc]init];
[arr addObject:temp];
}
b代码片段中我们把生成的对象装到了一个arr数组中
c.
NSMutableDictionary *dic = [[NSMutableDictionary alloc]init];
for (int i=0; i<5; i++) {
NSObject *temp = [[NSObject alloc]init];
[dic setObject:temp forKey:[NSString stringWithFormat:@"%d",i]];
}
d.
NSMutableSet *set = [[NSMutableSet alloc]init];
for (int i=0; i<5; i++) {
NSObject *temp = [[NSObject alloc]init];
[set addObject:temp];
}
通过上面的几个代码片段,我们知道对象在内存中的主要管理方式,NSManaged obejct它是数据的承载者,它在内存中也要被管理,如果它不被管理,当它的对象达到上千个,那么要想得到它,会是一个特别痛苦的是,于是它需要被管理,又因为它的特殊性:它有特征,它的数据值改变的跟踪,它的有效性的判断,它要被管理就要有一些相关的属性供管理者使用,于是CoreData框架就通过继承NSObject类实现NSManagedObject来成为被管理者对象。
三.NSManagedObjectContext管理者登场
NSManagedObject的出现注定它并不是在内存中存在供应用程序使用,它需要被持久化到永久磁盘中去,还需要跟踪它的属性变化,它的特征值的变化,属性的有效性,属性值的回滚与原子性访问,增加删除等复杂的一系列的操作,你想通过NSMutableArrary,NSMutableSet,NSMuatableDictionary的管理工具能胜任这份工作吗?答案肯定是不能的于是,CoreData就提供了一个能够胜任这份工作的人它就是NSManagedObjectContext,它就是NSManagedObject的大管家,它的主要任务是管理NSManagedObject的实例对象,增删改查追踪有效性判断等。应用程序想要得到任何对象都要通过对象管理上下问得到想要的对象。
从编程的角度来说,NSManagedObjectContext充当NSManagedObject的对象容器,缓冲区,通过该容器缓冲区可以对里面的对象进行各种操作,该缓冲区可以观察所有被装到它里面的所有对象的状态,编程者就可以通过Context来请求需要的数据,也可以通过Context持久化到永久磁盘上,也可以通过永久磁盘上的数据构造出NSManagedObject对象。
???????NSManagedObject如何和Context有关联了,Context又是如何操作NSManagedObject对象实例了??????
数组通过:[arr addObject:]把对象装进,先加入的对象的序列号就越小,可变数组可以通过方法删除指定的对象,通过指定的序列号可以得到想要的对象;
集合通过:[set addObject:]把对象装到集合中,集合是没有顺序的,可以通过遍历其中的对象从而得到自己想要的对象。
字典是通过:[dic setObject:obj forKey:key]把对象用键值对的方式把存储,通过键的方式把对象取出来。
因为CoreData为了实现持久化机制,它提供了很多实现来支持,数据实体必须是NSManagedObject或者其子类。如果你用一般的NSOject或者其子类创建出来的Model实体对象,Context就不能很好的管理,下面我们来讨论NSManagedObject如何被创建初始化和保存。
首先,NSManagedObject它是继承NSObject类,所以它和我们平时使用的类创建对象没有什么区别。为了支持被Context上下文管理,所以它和其他继承至NSObject的类最大的三个差异是:
a.被管理的对象必须是NSManagedObject或者其子类的实例对象;
b.创建的实例对象必须说明它被那个context所管理就是它被包含在那个context中;
c.必须有一个表述它的实体的属性的实体描述类,要不然context怎么会知道它有什么属性,属性的类型是日期,数组,字符串等了,所以必须有一个实体描述类NSEntityDescription。
创建NSManagerObject对象及把它关联到context的方法:
1.通过context的实例方法- (void)insertObject:(NSManagedObject *)object;把已经创建好的NSManagerObject对象家到context中,这样context就能管理该对象了
2.通过NSEntityDescription的类方法+ (id)insertNewObjectForEntityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context;其中entityName说明该NSManageObject是什么比如是“Book”的数据实体还是“Student”的数据实体,NSEntityDescription是对实体的描述。
上面的操作相当于我们把对象添加到一个内存缓存容器中。
数据模型的描述类NSEntityDescription类的登场:
数据实体Model有很多,如果一个工程中有十几个数据实体Model,它们之间又存在关系,CoreData给我们提供了一个强大的类NSEntityDescription类来管理我们的Model,通过NSentityDescription类我们就能够知道那个Model有什么属性,每个属性的默认设置的值,它们的取值范围等,强大的Xcode为我们提供了一个实体创建的可视化强大工具,通过它我们可以可视化创建Model和管理它们之间的关系。同样的我们也可以通过代码来创建(这种方法一定不是你喜欢的)
可视化新建:
代码新建NSEntityDescription:
NSEntityDescription *entry = [[NSEntityDescription alloc] init];
[entry setName:@"MyCustomEntry"];
[entry setManagedObjectClassName:@"MyCustomEntry"];
NSAttributeDescription *entryIdAttribute = [[NSAttributeDescription alloc] init];
entryIdAttribute.name = @"identifier";
entryIdAttribute.attributeType = NSStringAttributeType;
NSEntityDescription *element = [[NSEntityDescription alloc] init];
[element setName:@"MyCustomElement"];
[element setManagedObjectClassName:@"MyCustomElement"];
NSAttributeDescription *elementIdAttribute = [[NSAttributeDescription alloc] init];
elementIdAttribute.name = @"identifier";
elementIdAttribute.attributeType = NSStringAttributeType;
// To-many relationship from "Element" to "Entry":
NSRelationshipDescription *entriesRelation = [[NSRelationshipDescription alloc] init];
// To-one relationship from "Entry" to "Element":
NSRelationshipDescription *elementRelation = [[NSRelationshipDescription alloc] init];
[entriesRelation setName:@"entries"];
[entriesRelation setDestinationEntity:entry];
[entriesRelation setMinCount:0];
[entriesRelation setMaxCount:0]; // max = 0 for to-many relationship
[entriesRelation setDeleteRule:NSCascadeDeleteRule];
[entriesRelation setInverseRelationship:elementRelation];
[elementRelation setName:@"element"];
[elementRelation setDestinationEntity:element];
[elementRelation setMinCount:0];
[elementRelation setMaxCount:1]; // max = 1 for to-one relationship
[elementRelation setDeleteRule:NSNullifyDeleteRule];
[elementRelation setInverseRelationship:entriesRelation];
[entry setProperties:@[entryIdAttribute, elementRelation]];
[element setProperties:@[elementIdAttribute, entriesRelation]];
NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] init];
[mom setEntities:@[entry, element]];
上述两种方式是实体描述类的创建方式,和通过代码创建UI或者xib创建UI如出一辙。创建完成之后我们就得到了一个后缀名字为。。。.xcdatamodeld的文件,你通过可视化工具创建的实体都被归档到该文件中,就想Storyboard通过XML的形式归档一样可以当作资源文件,通过编译之后它又为我们生产了什么了,我们通过打开Mainbundle看里面多了些什么内容。
打开该文件看里面又什么东西:
上面的工作都是NSEntityDescription归档之后的表现形式,你要明确的知道的一个问题是NSEntityDescription对应的是一个实体类的描述,NSEntityDescription对应创建的表,我们创建的DataModel就是这些描述的归档形式,说直接一点就是DataModel中包含了你创建的实体的所有信息。这样的话NSManagedObjectModel类就隆重的登场了,该类就是DataMode的抽象,一个NSManagedObjectModel实例就代表了你创建的DataMode,DataMode与创建的表的关系就相当于NSManagedObjectModel与NSEntityDescription的关系。
下面我们来看NSManagedObjectModel的创建:
NSManagedObjectModel *modelObject= [[NSManagedObjectModel alloc] initWithContentsOfURL:@"该地址是编译之后DataModel生成的文件"];
到此我们都只是停留在内存层面上,内存中的对象要通过什么方式才能持久化到本地了????????????????
问题1:context怎么和本地的文件关联了
问题2:一个应用程序中可以有多个的数组,字典,集合等对象管理的实例对象,那么一个应用程序中就也可以有很多个的context,那么怎么管理它们了
通过context的头文件我们可以得到:
- (instancetype)initWithConcurrencyType:(NSManagedObjectContextConcurrencyType)ct
指定context的并发类型
/* coordinator which provides model and handles persistency (multiple contexts can share a coordinator) */
@property (strong) NSPersistentStoreCoordinator *persistentStoreCoordinator;
NSPersistenStoreCoordinator的到来给我们揭开这个谜底
WHY?NSPersistenStoreCoordinator
从中文的字面意思上来说,它是持久化协调器,从功能上来说它就是一个桥,一个把内存管理对象MO持久化到持久化存储器中的桥,一个context必须设置一个协调器,没有桥怎么过河。
下面我们开怎么在context和持久化存储器搭桥
- (instancetype)initWithManagedObjectModel:(NSManagedObjectModel *)model NS_DESIGNATED_INITIALIZER;
创建这个桥需要的一个参数是NSManagedObjectModel,说明并不是每个人都可以过这个桥的,你必须说清楚什么人可以过桥,model就是对可以过桥的一个描述(前面有讲解)
既然桥创建好了,那么这座桥有了,这座桥通往什么地方,肯定是通往持久化存储器了,于是持久化文件就出现了;
/* Adds the store at the specified URL (of the specified type) to the coordinator with the model configuration and options. The configuration can be nil -- then it's the complete model; storeURL is usually the file location of the database
*/
- (NSPersistentStore *)addPersistentStoreWithType:(NSString *)storeType configuration:(NSString *)configuration URL:(NSURL *)storeURL options:(NSDictionary *)options error:(NSError **)error;
其中参数storeURL就是持久化文件的存储位置。
这样Core Data的框架运行状态已经形成。
小结: