经典的数据库服务开发库有一下几种:
CoreData 操作较为复杂, MagicalRecord 有着很多的特性,比如可以根据设置在主线程或者子线程中进行操作,方便快捷,能入榜最佳10大开源库自有其独到的地方,会使用 MagicalRecord 需要具备一定的 CoreData 相关知识,本人也只是现学现用,但深知其可以为开发带来的好处,使用数据库的朋友有着如下的一些选择.
1. SQLite3 C函数形式(本人之前做过干嵌入式开发,即使是这样也不推荐使用面向过程毫无对象概念的SQLite3,有更好的方式为什么不用呢?)
2. FMDB 对SQLite3的封装,有着对象的概念,熟悉SQ语句的朋友可以使用,但还没有做到对象与记录实时对应
3. CoreData 他做到了对象与记录实时对应关系,使用其自身的搜索体系(不用SQ语言),但其基本的操作以及配置让人望而却步
4. MagicalRecord 对 CoreData 官方的用法进行了人性化的封装,用过 CoreData 基本操作再使用 MagicalRecord 会深有体会
5. ObjectiveRecord 也是对 CoreData 的人性化封装,使用更加傻瓜,但傻瓜的代价就是牺牲了一些更强大的功能,在Github上搜索关键字即可
Realm
MagicalRecord
MagicalRecordDemo
下边先介绍Realm
Realm : Realm 是一个跨平台的移动数据库引擎.Realm 并不是对 Core Data 的简单封装.
相反地, Realm 并不是基于 Core Data ,也不是基于 SQLite 所构建的。它拥有自己的数据库存储引擎,可以高效且快速地完成数据库的构建操作。
环境配置:
开始使用:
@interface Dog : RLMObject
@property NSString *name;
@property NSData *picture;
@property NSInteger age;
@end
@implementation Dog
@end
RLM_ARRAY_TYPE(Dog)
@interface Person : RLMObject
@property NSString *name;
@property RLMArray<Dog *><Dog> *dogs;
@end
@implementation Person
@end
Dog *mydog = [[Dog alloc] init];
mydog.name = @"Rex";
mydog.age = 1;
mydog.picture = nil; // properties are nullable
NSLog(@"Name of dog: %@", mydog.name);
RLMResults<Dog *> *puppies = [Dog objectsWhere:@"age < 2"];
puppies.count; // => 0 because no dogs have been added to the Realm yet
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addObject:mydog];
}];
puppies.count; // => 1
dispatch_async(dispatch_queue_create("background", 0), ^{
Dog *theDog = [[Dog objectsWhere:@"age == 1"] firstObject];
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
theDog.age = 3;
[realm commitWriteTransaction];
});
更多信息请点击 :realmAPI集合
Magicalrecord
Cocoa中存在一种技术叫Core Data,用来对数据进行持久化,类似于Java世界中的Hibernate。
在新建Cocoa Application/iOS Application的向导中,有一个选项是要不要使用Core Data,当启用以后你会发现在AppDelegate.m中添加了大量与Core Data相关的代码,但是你对大部分代码不知所以然。
MagicalRecord 受Ruby on Rails活动记录获取方式的便利性影响.项目目标是:
1:清理我的Core Data相关代码
2:支持清晰,简单,一行代码式的查询
3:当需要优化请求时,仍然可以修改 NSFetchRequest.
Magical Record的出现在一定程度上缓解了这个问题,降低了Core Data的使用门槛。Magical Record借用了Ruby on Rails中的Active Record模式,使得你可以非常容易的添加、查找、删除数据。
AppDelegate.m中添加以下代码对Magical Record进行初始化
启动时MR_mergedObjectModelFromMainBundle方法报错Core Data的模型有版本的概念,有可能在你Magical Record第一次初始化完成以后,你又更改了模型文件,导致Core Data去合并模型报错。解决办法很简单,点击菜单中的Project->Clean即可。项目使用ARC后,编译Magical Record不通过点击项目 -> Build Phases -> Compile Sources中, 双击报错的class文件, 编辑Compiler Flags加入 -fno-objc-arc。
环境配置:
使用 CocoaPods 安装
把下面一行添加到 Podfile:
pod "MagicalRecord"
在工程目录执行: pod install
现在你可以添加#import <MagicalRecord/MagicalRecord.h>到任意项目源文件中,并开始使用MagicalRecord!
在工程中创建实例模型
以定义实体 "Person"为例,它有属性age, firstName和lastName.
创建一个新的数据模型,命名为TestModel(File --> New --> File-->Core Data > Data Model)
添加一个新的实体,名为Person(Add Entity)
添加属性age (Integer16), firstName (String)和 lastName (String) 4.创建 NSManagedObject (Editor > Create NSManagedObject Subclass… > Create)子类以更好地管理我们的实体
在AppDelegate中创建初始化数据库
具体使用方式如下:
- (void)magicaRecord{
//创建上下文管理类
for (NSInteger i = 0; i<10; i++) {
//为了创建并插入一个新的实体实例到默认上下文对象中
Person *person = [Person MR_createEntity];
person.age =@(25+i);
person.firstName = [NSString stringWithFormat:@"JSKKSK%ld",i];
person.lastName = [NSString stringWithFormat:@"XXMMXM%ld",i];
[[NSManagedObjectContext MR_defaultContext] MR_saveOnlySelfAndWait];
}
NSArray *personArray = [Person MR_findAll];
if (personArray.count>0) {
for (Person *person in personArray) {
NSLog(@"firstName:%@ lastName:%@ age:%@",person.firstName,person.lastName,person.age);
}
}
NSLog(@"--------------------------------");
NSArray *findAttributeArray = [Person MR_findByAttribute:@"age" withValue:@(25) andOrderBy:@"age" ascending:NO];
if (findAttributeArray.count>0) {
for (Person *person in findAttributeArray) {
NSLog(@"firstName:%@ lastName:%@ age:%@",person.firstName,person.lastName,person.age);
}
}
NSLog(@"--------------------------------");
Person *firstData= [Person MR_findFirst];
NSLog(@"firstName:%@ lastName:%@ age:%@",firstData.firstName,firstData.lastName,firstData.age);
NSLog(@"--------------------------------");
NSArray *deleteArray = [Person MR_findByAttribute:@"age" withValue:@(28) andOrderBy:@"age" ascending:NO];
if (deleteArray.count>0) {
Person *tempPerson = deleteArray[0];
[tempPerson MR_deleteEntity];
[[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
}
NSLog(@"--------------------------------");
NSArray *personArrays = [Person MR_findAll];
if (personArrays.count>0) {
for (Person *person in personArrays) {
NSLog(@"firstName:%@ lastName:%@ age:%@",person.firstName,person.lastName,person.age);
}
}
}
NSLog(@"--------------------------------");
// 获取上下文环境
NSManagedObjectContext *defaultContext = [NSManagedObjectContext MR_defaultContext];
// 在当前上下文环境中创建一个新的 Person 对象.
Person *person = [Person MR_createEntityInContext:defaultContext];
person.firstName = @"firstname";
person.lastName = @"lastname";
person.age = @100;
// 保存修改到当前上下文中.
[defaultContext MR_saveToPersistentStoreAndWait];
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){
Person *localPerson = [person MR_inContext:localContext];
localPerson.firstName = @"Yan";
localPerson.lastName = @"Feng";
}];
NSArray *personArray = [Person MR_findAll];
if (personArray.count>0) {
for (Person *person in personArray) {
NSLog(@"firstName:%@ lastName:%@ age:%@",person.firstName,person.lastName,person.age);
}
}
NSLog(@"--------------------------------");
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){
Person *localPerson = [person MR_inContext:localContext];
localPerson.firstName = @"Yan";
localPerson.lastName = @"Feng";
} completion:^(BOOL success, NSError *error) {//这个完成的block,在主线程(队列)中调用,所以可以在此block里安全触发UI更新.
NSArray * persons = [Person MR_findAll];
[persons enumerateObjectsUsingBlock:^(Person * obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"firstname: %@, lastname: %@\\n", obj.firstName, obj.lastName);
}];
}];
操作被管理的对象上下文
对象上下文环境是你操作Core Data内数据的基础,只有正确获取到了上下文环境,才有可能进行相关的读写操作.
换句话说,程序的任意位置,只要能正确获取上下文,都能进行Core Data的操作.这也是使用Core Data共享数据的基础之一.相较于传统的方式,各个页面之间只需要与一个透明的上下文环境进行交互,即可进行页面间数据的共享.
创建新的对象上下文
许多简单的类方法可以用来帮助你创建一个新的对象上下文:
+ [NSManagedObjectContext MR_context]: 设置默认的上下文为它的父级上下文.并发类型为 #NSPrivateQueueConcurrencyType.
+ [NSManagedObjectContext MR_newMainQueueContext]: 并发类型为 ** #NSMainQueueConcurrencyType**.
+ [NSManagedObjectContext MR_newPrivateQueueContext]: 并发类型为 #NSPrivateQueueConcurrencyType.
+ [NSManagedObjectContext MR_contextWithParent:…]: 允许自定义父级上下文.并发类型为 #NSPrivateQueueConcurrencyType.
+ [NSManagedObjectContext MR_contextWithStoreCoordinator:…]:允许自定义持久化存储协调器.并发类型为 #NSPrivateQueueConcurrencyType.
保存对象
通常,你的应用应该在数据变化时,将其保存到持久化存储层中.有些应用选择仅在应用结束时保存,但是在大多数情况下并不需要这样做 - 实际上,如果你仅在应用退出时保存数据,很有可能会丢失数据!如果你的应用闪退了,会生什么?用户会丢失所有已经保存的数据 - 这是一种非常糟糕的用户体验,却又很容易避免.
如果你发现保存操作耗费了很长时间,你应该考虑使用一些方式优化:
在后台线程保存: MagicalRecord 提供了一种简捷的API来改变并立即在后台线程保存数据 - 例如:
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
} completion:^(BOOL success, NSError *error) {
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
把任务分割成小块的保存任务: 某些数据量较大的任务,如导入大量的数据,应该被分割成更小块的保存任务.没有统一的标准规定单次保存多少任务最合适,所以你需要使用工具来测试你的应用工的性能以针对自己的应用进行调整.工具可选使用 Apple的 Instruments.
处理需要长时间运行的保存任务
当iOS应用退出时,有一个较短的时间来整理和保存数据到磁盘.如果你确定某个保存操作很可能会花费一定时间,最好的方式是请求延长应用的生命周期,比如这样:
UIApplication *application = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
实际中,我们通过MJExtension数据解析,那么如何导入到MagicalRecord中(导入数组)
由一个JSON数组提供的一组数据或者正在导入大量的单一类型数据的情况,很常见.导入这样的一组数据的具体实现细节,由+MR_importFromArray:类方法中能找到.
NSArray *arrayOfPeopleData = /// result from JSON parser
NSArray *people = [Person MR_importFromArray:arrayOfPeopleData];
这个方法,和 +MR_importFromObject:一样,也是同步的,所以应该使用前面提到的后台执行block的方式来导入数据.
[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *)localContext {
Person *importedPerson = [Person MR_importFromObject:personRecord inContext:localContext];
}];