iOS 数据库实战

经典的数据库服务开发库有一下几种:

 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.png

Realm : Realm 是一个跨平台的移动数据库引擎.Realm 并不是对 Core Data 的简单封装.
相反地, Realm 并不是基于 Core Data ,也不是基于 SQLite 所构建的。它拥有自己的数据库存储引擎,可以高效且快速地完成数据库的构建操作。

环境配置:

环境配置.png

开始使用:

@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)子类以更好地管理我们的实体
person.png
NSManagedObject.png

在AppDelegate中创建初始化数据库


MagicalRecord.png

具体使用方式如下:

    - (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];
}];
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,423评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,147评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,019评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,443评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,535评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,798评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,941评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,704评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,152评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,494评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,629评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,295评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,901评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,978评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,333评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,499评论 2 348

推荐阅读更多精彩内容