
课程咨询: 400-996-5531 / 投诉建议: 400-111-8989
认真做教育 专心促就业
看了这篇文章的标题,也许有些人还不知道Realm是什么,那么武汉达内ios培训先简单介绍一下这个新生的数据库。号称是用来替代SQLite和CoreData的。Realm有以下优点:
使用方便
Realm并不是基于SQLite的对象关系映射数据库。它是使用自己的持久化引擎,为简单和速度而生。用户们说,他们在数分钟之内就上手了Realm,构建一个app只需要数小时,每个app开发时间至少节约数周的时间。
快
Realm比其他的对象关系映射型数据库(ObjectRelationalMapping),甚至比原生的SQLite更加快,这都得益于它零拷贝的设计。看看iOS用户和Android用户都是怎么评价它的快的Twitter
跨平台
Realm支持iOS和OSX(Objective?C&Swift)和Android。你可以通过使用相同的model,共享Realm文件到各个平台,Java,Swift,Objective-C。并且在全平台可以使用相同的业务逻辑
优秀的特性
Realm支持先进的特性,如加密,图形查询,轻松的迁移。Realm的API是一个非常适合打造高响应的应用程??序,并且Realm为我们提供方便的组件,以轻松构建复杂的用户界面
值得信任
Realm已经获得了银行,医疗保健提供商,复杂的企业app,星巴克这些产品的青睐。
社区驱动
Realm是Github上星标多的数据库里面排名四,仅次于Java和Cocoa的repos。除了核心工程之外,Realm的社区已经编译了上百个app插件和组件
支持
可以从Realm公司快速获得官方的答案,去编译和支持你的数据库。Realm的团队会在Github,StackOverflow,&Twitter回答大家的各种问题
把一个使用coredata框架作为数据库存储方式的app,迁移到Realm的确是一件很容易的事情。如果你现在有一个已经用了CoreData的app,并且考虑换成Realm,这个手把手教程正适合你!
很多开发者在用户界面,高度集成了CoreData(有时可能有上千行代码),这时很多人会告诉你转换CoreData到Realm可能会花数小时。CoreData和Realm两者都是把你的数据当成Object看待,所以迁移通常是很直接的过程:把你已经存在的CoreData的代码重构成使用RealmAPI的过程是很简单的。
迁移后,你会为Realm为你app带来的易用性,速度快,和稳定性而感到兴奋。
1.移除CoreDataFramework
先,如果你的app当前正在使用CoreData,你需要找出哪些代码是包含了CoreData的代码。这些代码是需要重构的。幸运的是,这里有一个手动的方式去做这件事:你可以手动的在整个代码里面搜索相关的代码,然后删除每个导入了CoreData头文件声明的语句
#import
//or
@importCoreData;
一旦这样删除以后,每一行使用了CoreData的将会报一个编译错误,接下来,解决这些编译错误只是时间问题。
2.移除CoreData的设置代码
在CoreData中,对modelobjects的更改是要通过managedobjectcontextobject来实现的。而managedobjectcontextobjects又是被persistentstorecoordinatorobject创建的,它们两者又是被managedobjectmodelobject创建的。
可以这么说,在你开始思考用CoreData读取,或者写入数据的时候,你通常需要在你的app中的某处去设置依赖的对象,暴露一些CoreData的方法给你的app逻辑使用。无论在你的applicationdelegate中,全局的单例中,或者就是在inline实现中,这些地方都会存在大量的潜在的CoreData设置代码。
当你准备转换到Realm时,所有的这些代码都可以删掉。
在Realm中,所有设置都在你一次创建一个Realmobject的时候就已经都完成了。当然也是可以手动去配置它,就像你指定Realm数据文件存储在你的硬盘的哪个路径下,这些完全都可以在runtime的时候去选择的。
RLMRealm*defaultRealm=[RLMRealmdefaultRealm];
//or
letrealm=Realm()
感觉很好吧?
3.迁移model文件
在CoreData中,实用的那些类都是被定义成NSManagedObject的子类。这些object的接口都是很标准的,原始的类型(比如NSInteger和CGFloat)是不能被使用的,它们必须抽象成一个NSNumber对象。
@interfaceDog:NSManagedObject
@property(nonatomic,copy)NSString*name;
@property(nonatomic,strong)NSNumber*age;
@property(nonatomic,strong)NSDate*birthdate;
@end
@implementationDog
@dynamicname;
@dynamicage;
@dynamicbirthdate;
@end
把这些managedobjectsubclasses转换成Realm是非常简单的:
@interfaceDog:RLMObject
@propertyNSString*uuid;
@propertyNSString*name;
@propertyNSIntegerage;
@propertyNSDate*birthdate;
@end
@implementationDog
+(NSString*)primaryKey
{
return@"uuid";
}
+(NSDictionary*)defaultPropertyValues
{
return@{@"uuid":[[NSUUIDUUID]UUIDString],
@"name":@"",
@"birthdate":[NSDatedate]};
}
@end
看这些实现,还是有一些Realm的细节需要注意的。
对于初次使用Realm的人来说,没有必要去指定属性关键字,Realm在内部已经管理了。所以这些类的头文件看上去都很精简。此外,Realm支持简单的数据类型,比如NSInteger和CGFloat,所有所有的NSNumber都可以安全的删除。
另一方面,这有一些关于Realmmodel的声明额外的说明。
CoreDataobjects通过内部的NSManagedObjectID属性去标识一个objects,Realm把这个留给开发者去完成。在上面的例子中,我们额外添加了一个名为uuid的属性,然后通过调用[RLMObjectprimaryKey]方法去作为这个class的标识。当然,如果你的objects完全不需要标识,这些都可以跳过。
在写数据的过程中(这个过程不会太长!),Realm不能处理nil的object的属性。原因是,在[RLMObjectdefaultPropertyValues]这个类方法中给每个object在初创建的时候,每个object属性都定义了一系列default值。当然这只是暂时的,我们很高兴的告诉你,在接下来的更新中,我们将会支持Realmobject的属性可以为nil。
4.迁移写操作
如果你不能保存你的数据,这肯定不是一个持久的方案!创建一个新的CoreData对象然后再简单的修改一下它,需要下面这些代码:
//CreateanewDog
Dog*newDog=[NSEntityDescriptioninsertNewObjectForEntityForName:@"Dog"inManagedObjectContext:myContext];
newDog.name=@"McGruff";
//SavethenewDogobjecttodisk
NSError*saveError=nil;
[newDog.managedObjectContextsave:&saveError];
//RenametheDog
newDog.name=@"Pluto";
[newDog.managedObjectContextsave:&saveError];
相比之下,Realm保存的操作是略有不同的,但在相同的范围内修改上面的代码,仍然有相似的地方。
//Createthedogobject
Dog*newDog=[[Dogalloc]init];
newDog.name=@"McGruff";
//SavethenewDogobjecttodisk(Usingablockforthetransaction)
RLMRealm*defaultRealm=[RLMRealmdefaultRealm];
[defaultRealmtransactionWithBlock:^{
[defaultRealmaddObject:newDog];
}];
//Renamethedog(Usingopen/closemethodsforthetransaction)
[defaultRealmbeginWriteTransaction];
newDog.name=@"Pluto";
[defaultRealmcommitWriteTransaction];
完成!我们的数据被保存了!
明显的不同是,在Realm中,一旦一个objects被添加到一个Realmobject中,它就是不可被修改的。为了在修改属性操作的后面执行,Realmobject会被保存在一个写的事务中。这种不能被修改的model,保证了在不同线程中读/写object数据的情况下,数据的一致性。
CoreData的实现确实可以改变属性,然后调用save方法,对比Realm的实现,只是一些小小的不同罢了。
5.迁移查询
另一方面,如果你不能检索查询你的数据,这肯定不是一个持久的方案!
在CoreData的基础实现中,它运用了fetchrequests的概念去从硬盘检索数据。一个fetchrequestobject是被当成一个单独的实例化对象去创建的,包含了一些额外的过滤参数,排序条件。
NSManagedObjectContext*context=self.managedObjectContext;
//Afetchrequesttogetalldogsyoungerthan5yearsold,inalphabeticalorder
NSEntityDescription*entity=[NSEntityDescription
entityForName:@"Dog"inManagedObjectContext:context];
NSPredicate*predicate=[NSPredicatepredicateWithFormat:@"age<5"];
NSSortDescriptor*sortDescriptor=[[NSSortDescriptoralloc]initWithKey:@"name"ascending:YES];
NSFetchRequest*request=[[NSFetchRequestalloc]init];
request.entity=entity;
request.predicate=predicate;
request.sortDescriptors=@[sortDescriptor];
NSError*error;
NSArray*dogs=[mocexecuteFetchRequest:requesterror:&error];
虽然这确实挺好,但是需要编写大量的代码!一些聪明的开发者就开发了一些library使这些代码编写的更加容易。比如MagicalRecord。
对比这些,使用了Realm之后,这些查询的等效代码如下:
RLMResults*dogs=[[DogobjectsWhere:@"age<5"]sortedResultsUsingProperty:@"name"ascending:YES];
在一行调用了2个方法。对比CoreData将近10行代码。
当然,相同操作得到的结果是相同的(RLMResults和NSArray基本类似),转换到Realm,由于这些查询都是很独立的,所以查询周围的逻辑只需要重构很少的一部分代码就可以了。
6.迁移用户数据
一旦你所有代码都迁移到Realm,这里还有一个突出的问题,你如何迁移所有用户已经存在在他们设备上的数据,从CoreData迁移到Realm中?
显然,这是非常复杂的问题,它决定于你的app的功能,还有用户的环境。你处理这种情况可能解决办法每次都不一样。
目前,我们看到了2种情况:
一旦你迁移到Realm,你可以重新导入CoreDataframework到你的app,用原生的NSManagedObjectobjects去fetch你的用户的CoreData数据,然后手动的把数据传给Realm。你可以把这段迁移的代码永久的留在app中,或者也可以经过非常充足的时间之后,再删除掉。
如果用户数据不是不可替代的——举个例子,如果是一些简单的缓存信息,可以通过硬盘上的用户数据重新生成的话,那么可以很简单的就把CoreData数据直接清除掉,当用户下次打开app的时候,一切从0开始。当然这需要经过非常谨慎的考虑,不然的话,会给很多人留下非常坏的用户体验。
终,决定应该偏向于用户。理想的情况是不要留下CoreData还连接着你的app,但是结果还是要取决于你的情况。好运!
进一步的讨论
虽然在移植一个应用程序到Realm过程中,没有真正重要的步骤,但是有一些额外的情况下,你应该知道:
并发
如果你在后台线程做了一些比较重的操作,你可能会发现你需要在线程之间传递Realmobject。在CoreData中允许你在线程之间传递managedobjects(虽然这样做不是佳实践),但是在Realm中,在线程中传递objects是严格禁止的,并且任何企图这样做的,都会抛出一个严重的异常。
如此来说,对于下面这些情况,是件很容易的事情:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0),^{
//Renamethedoginabackgroundqueue
[[RLMRealmdefaultRealm]transactionWithBlock:^{
dog.name=@"PeanutWigglebutt";
}];
//Printthedog'snameonthemainqueue
NSString*uuid=dog.uuid;
dispatch_async(dispatch_get_main_queue(),^{
Dog*localDog=[DogobjectForPrimaryKey:uuid];
NSLog(@"Dog'snameis%@",localDog.name);
});
});
虽然Realmobjects不能在线程间被传递,但是Realmproperties的副本可以在线程中被传递。考虑到Realm从磁盘中检索objects是非常快速的,如果只是简单的通过新线程在存储区中重新refetch相同的object,这只会造成很小的性能损失。在这个例子中,我们取了对象的主键的copy,然后把它从后台队列传递给主队列,然后再通过它在主线程的上下文中重新获取该对象。
NSFetchedResultsController的等效做法
相比CoreData的所有缺点,可能使用CoreData充足的理由就是NSFetchedResultsController——这是一个类,它可以检测到数据存储的变化,并且能自动的把这一变化展示到UI上。
在写这篇文章的时候,Realm还没有相似的机制。虽然它可以注册一个block,这个block会在数据源发生变化的时候被执行,但是这种"蛮力"的做法对大多数的UI来说都是不友好的。目前,如果你的UI代码很依赖Realm,那么这种做法对你来说就像处理一个breaker一样。
Realm的cocoa工程师现在正在开发一套通知系统,当一些object的属性被更改的时候,允许我们去注册一个通知,来接收到这些改变。这些特性都会在Realm的SwiftandObjective-C的未来的更新版本中。
在此期间,如果现有的通知blockAPI还是没有满足你的需要,但是你还是需要当特定的property被更改了收到一个通知,这里推荐使用神奇的三方库,名字叫RBQFetchedResultsController,它能模仿上述功能。除此之外,你还可以通过在objects里面加入setter方法,当setter方法被调用的时候,发送一个广播通知,这样做也能实现相同的功能。
CoreData和Realm的在展示数据的时候都是通过modelobjects,由于这一相似性,得以让我们从CoreData迁移到Realm时非常迅速,简单(并且非常令人满意!)。尽管开始看上去令人怯步,但是实际做起来,就是需要把每个CoreData的方法调用转换成等价的Realm的方法,然后写一个辅助类去帮你迁移用户的数据。这些也都非常简单。
如果你在你的app中使用CoreData遇到了些困难,需要些更加简单的解决办法,我们强烈推荐你尝试一下Realm,看看它是否适用于你。