Realm的学习笔记—1
这篇直接搬了一份官方文档过来看的 由于之前没用markdown搞的乱七八糟的 所以重新做了一份 后面看到官网的中文文档更新不及时看着英文翻译了一点 搞的更乱了 :( 英文好的直接点右边->官方OC文档
Realm
是一个移动端的数据库,Realm
是SQLite
和CoreData
的替代者。它可以节省你成千上万行代码和数周的工作,并且让你精巧的制作出令人惊叹的用户体验。
文档版本 0.93.2
在github上获取
需求:
iOS7以上 OS X10.9以上 WatchKit.
Xcode 6 及以上 必需.
支持Objective-C Swift 1.2 Swift 2.0
安装
静态库安装 (Objective-C & Swift)
1.下载最新的Realm
发行版本并在本地解压。
2.从 ios/static/
目录里,把Realm.framework
文件拖动到你的Xcode开发项目里的File Navigator
中。 确保Copy items into destination group’s folder
已经被选中,按Finish。
3.在Xcode file explorer
中选中你要的开发项目. 选择target
,点击Build Phases
选项. 在Link Binary with Libraries
里按+,添加libc++.dylib
。
4.如果使用Realm + Swift
,拖动Swift/RLMSupport.swift
到你的Xcode project
的File Navigator
中,点选Copy items if needed
。
5.如果在OSX项目中使用Realm,点击左上角的 + ,选择New Copy Files Phase
,将其重命名为Copy Frameworks
, 将Destination设置为Frameworks,并且添加Realm.framework
。
动态库安装 (Objective-C & Swift)
1.下载最新的Realm
发行版本并在本地解压。
2.来到Xcode工程的General
设置界面,从 ios/dynamic/
或者osx/
目录里,把Realm.framework
文件拖动到Embedded Binaries
里面。 确保Copy items into destination group’s folder
已经被选中,按Finish。
3.In your unit test target’s “Build Settings”, add the parent path to Realm.framework in the “Framework Search Paths” section.
4.If using Realm with Swift, drag the file at Swift/RLMSupport.swift into the File Navigator of your Xcode project, checking the Copy items if needed checkbox.
5.If using Realm in an iOS 8 project, create a new “Run Script Phase” in your app’s target’s “Build Phases” and paste the following snippet in the script text field: bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework/strip-frameworks.sh" This step is required to work around an App Store submission bug when archiving universal binaries.
通过CocoaPods安装 (Objective-C Only)
如果你使用CocoaPods…
1.把pod "Realm"
添加到你的Podfile中。
2.在命令行中执行pod install
。
3.将CocoaPods
生成的.xcworkspace
运用到你的开发项目中即可。
Xcode 插件
我们的Xcode插件让新建Realm模型model
很轻松。安装Realm Xcode插件的最简单的途径就是通过Alcatraz在RealmPlugin
目录下。
你也可以自行手动安装:打开release zip
中的plugin/RealmPlugin.xcodeproj
, 点击编译build
。 重启Xcode生效。
如果你使用Xcode新建文件File > New > File… — or ⌘ N
,可以看到有一个新建Realm模型create a new realm model
的选项。
Realm浏览器/数据库管理器
我们另外提供了一个独立的数据库管理工具,用来查看和编辑realm数据库.realm
。
你可以从browser/
的release zip
目录下找到它。
使用菜单中的工具tool
>生成演示数据库generate demo database
, 你可以生成一个测试数据库(当然里面的数据是样本数据).
API 参考
所有的类和方法什么的都可以去API文档查阅
示例
你可以在examples/
路径里面找到一个文件,叫做release zip
。 里面包含了Objective-C
, Swift
和RubyMotion
的示例程序。 它们演示了Realm
得很多功能和特性,例如数据库迁移,如何与UITableViewController’s
一起使用,加密等等。
数据模型(model)
Realm的数据模型是用传统的Objective-C 接口interface
和@properties
定义的。 就只要定义RLMObject
的一个子类subclass
或者一个现成的模型类,你就能轻松创建一个Realm
的数据模型对象data model object
。Realm
模型对象和其他的objective-c
的功能很相似–你可以给它们添加你自己的方法method
和协议protocol
然后和其他的对象一样使用。
唯一的限制就是从它们被创建开始,只能在一个线程中被使用。
如果你已经安装了我们的Xcode
插件 那么,在New File
对话框中会有一个很漂亮的样板,你可以用它来创建interface
和implementation
文件。
你只需要为对象列表添加目标类型的属性property
或者RLMArray
的,就可以创建数据库关联和嵌套数据结构
<code>
#import <Realm/Realm.h>
@class Person;
// Dog model
@interface Dog : RLMObject
@property NSString *name;
@property Person *owner;
@end
RLM_ARRAY_TYPE(Dog) // define RLMArray<Dog>
// Person model
@interface Person : RLMObject
@property NSString *name;
@property NSDate *birthdate;
@property RLMArray<Dog> *dogs;
@end
RLM_ARRAY_TYPE(Person) // define RLMArray<Person>
// Implementations
@implementation Dog
@end // none needed
@implementation Person
@end // none needed
</code>
RLMObject的相关细节.
属性property
种类
Realm支持以下的属性property
种类: BOOL
,bool
, int
, NSInteger
, long
, float
, double
, CGFloat
, NSString
, NSDate
和NSData
。
你可以使用RLMArray<NSObject>
和RLMObject
来模拟对一或对多的关系
Realm
也支持RLMObject
继承。
属性property
特性
请注意Realm
忽略了objective-c
的property attributes
,像nonatomic
, atomic
, strong
, copy
, weak
等等。
所以,为了避免误解,我们推荐你在写入模型的时候不要使用任何的property attributes
。但是,假如你设置了,这些attributes
会一直生效直到RLMObject
被写入realm
数据库。
无论RLMObject
在或不在realm
中,你为getter
和setter
自定义的名字都能正常工作。
数据模型定制
几个存在的类方法进一步指定模型信息:
+attributesForProperty:
可以被重写来来提供特定属性property
的属性值attrbutes
例如某个属性值要添加索引。
+defaultPropertyValues
可以被重写,用以为新建的对象提供默认值。
+primaryKey
可以被重写来设置模型的主键。定义主键可以提高效率并且确保唯一性。
ignoredProperties
可以被重写来防止Realm
存储模型属性。
存储对象
对对象的所有更改(添加,修改 和删除)都必须通过写入事务完成。
Realm
的对象可以被实例化并且被单独使用,和其他常规对象无异。
如果你想要在多个线程中共享或者永久保存以重复使用对象,你必须将其存储到Realm数据库中——这个操作必须在写事务中完成。 你可以参照如下代码添加一个对象:
创建一个对象
Person *author = [[Person alloc] init]; author.name = @"David Foster Wallace";
获取一个默认realm对象
RealmRLMRealm *realm = [RLMRealm defaultRealm];
你只须这么做一次(单线程操作) Add to Realm with transaction
<code>
[realm beginWriteTransaction];
[realm addObject:author];
[realm commitWriteTransaction];
</code>
等到你把这个对象添加到realm数据库里面之后, 你可以在多个线程里面共享之。
并且从现在开始,你所做的每一次更改(必须在一个写事务中完成)也会被永久储存。
等到写事务完成,这个更改将对所有共享这个Realm数据库的线程可见。
需要注意的是,写入操作会相互阻塞,而且其相对应的进程也会受到影响。
这和其他的永久数据存储解决方案是一样的,所以我们建议你使用常用的,也是最有效的方案, 将所有写入放到一个单独的进程中。
还要注意的是,因为realm的MVCC结构, 读取并不会 因为一个进行中的写事务而受到影响。
详细内容,请阅读RLMObject。
查询
所有的数据抓取都很简单,并且直到获得数据之后才创建副本。
关于使用RLMResults的小贴士:
Realm的对象查询返回一个RLMResults对象。它包含了一系列的RLMObject。
RLMResults有一个与NSArray很相似的interface(接口)并且对象可以通过索引(index)下标获取。
但不同于NSArrays的是,RLMResult是归类的——它只能容纳一种RLMObjects类型。
根据种类获取对象从realm中获取对象的最基本方法就是 [RLMObject allObjects], 它返回一个RLMResults,里面是查询的子类的所有RLMObject实例。
// 默认查询 RealmRLMResults *dogs = [Dog allObjects]; // 从默认Realm中查找所有的🐶
<code>
// 指定查询
RealmRLMRealm *petsRealm = [RLMRealm realmWithPath:@"pets.realm"]; // 得到一个指定的realm
RealmRLMResults *otherDogs = [Dog allObjectsInRealm:petsRealm]; // 在指定的realm中查询所有的狗
</code>
谓词/条件查询
如果你对 NSPredicate很熟悉的话, 那么你就已经知道怎么在realm里面查询了。
RLMObjects
, RLMRealm
, RLMArray
和RLMResults
都提供很好的methods
来查询特定的RLMObjects
:
你只需要传递相应地NSPredicate
实例,谓词字符串,谓词格式字符串,就可以获取你想要的RLMObjects
实例。就和NSObject
一样的。
举个例子,下面的代码就是对上面的拓展。 通过调用[RLMObject objectsWhere:]
,获得了默认realm数据库中的所有颜色是黄褐色的,名字开头是“B”的狗的实例。
<code>
// 条件查询
RLMResults *tanDogs = [Dog objectsWhere:@"color = 'tan' AND name BEGINSWITH 'B'"];
// 使用一个NSPredicate对象查询
NSPredicate *pred = [NSPredicate predicateWithFormat:@"color = %@ AND name BEGINSWITH %@", @"tan", @"B"];
tanDogs = [Dog objectsWithPredicate:pred];
</code>
可以参看Apple的Predicates Programming Guide 了解更多关于如何创建谓词。
Realm支持很多常见的谓词:在比较中, 操作数可以是属性名或者常量。但是其中至少有一个是属性名。
只有int, long, float, double, and NSDate这些属性类型(property types)支持 ==, <=, <, >=, >, !=, 和 BETWEEN这些比较操作符。布尔属性可以支持==和!=。
在NSString和NSData属性中, 我们支持的操作符有 ==, !=, BEGINSWITH, CONTAINS和ENDSWITH。
realm还支持如下的复合型操作符: AND, OR, NOT注意,我们虽然不支持aggregate expression type,但是我们支持BETWEEN操作符, 例如:
RLMResults *results = [Person objectsWhere:@"age BETWEEN %@", @[42, 43]];
详询[RLMObject objectsWhere:]
.
条件排序
在很多情况下,我们都希望获取或者查询返回的结果都能按照一定条件排序。
所以,RLMArray支持使用指定的属性对数据列进行排序。
Realm允许你指定一个排序要求并且根据一个或多个属性进行排序。
举例来说, 下面代码呼叫了[RLMObject objectsWhere:where:]
对返回的数据”dogs”进行排序,排序的条件是名字的字母表升序。:
<code>
// Sort tan dogs with names starting with "B" by name
RLMResults *sortedDogs = [[Dog objectsWhere:
@"color = 'tan' AND name BEGINSWITH 'B'"] sortedResultsUsingProperty:@"name" ascending:YES];
</code>
链式查询
Realm查询引擎的一个独特属性就是它能够进行简单快捷的链式查询, 而不需要像传统数据库一样的麻烦。举个例子来说,假如你要所有黄褐色的小狗的结果序列,然后从中查找名字开头是“B“的小狗。 你可以发送如下的请求。
<code>
RLMResults *tanDogs = [Dog objectsWhere:@"color = 'tan'"];
RLMResults *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH 'B'"];
</code>
Realm数据库
默认的realm数据库
你可能已经注意到了我们总是通过[RLMRealm defaultRealm]
来获得realm数据库。
这个method返回一个RLMRealm对象,指向一个叫做”default.realm“的文件。
这个文件在你的app的Documents文件夹里面。
其他的realm数据库
有的时候拥有多个分离的数据库是非常有必要的。
例如,如果你需要绑定数据到一个App,你可以打开另一个只读的Realm数据库,参考[RLMRealm realmWithPath:]
和 [RLMRealm realmWithPath:readOnly:error:]
请注意传递到[RLMRealm realmWithPath:]
的路径必须是有写入权限的。
存储可写Realm数据库的最常用路径就是iOS的“Documents”和OSX的“Application Support”。
跨线程使用数据库
在不同的线程间使用同一个Realm数据库,你必须呼叫[RLMRealm defaultRealm]
,[RLMRealm realmWithPath:]
或者 [RLMRealm realmWithPath:readOnly:eror:]
以得到个线程相对应的realm对象。
只要你路径一致,所有的RLMRealm对象指向的文件就一致。
我们还不支持跨线程共享RLMRealm对象。
纯内存数据库
正常的Realm数据库是存储在硬盘上的, 但你也可以通过使用
[RLMRealm inMemoryRealmWithIdentifier:]
来创建一个纯内存数据库。
<code>
RLMRealm *realm = [RLMRealm inMemoryRealmWithIdentifier:@"MyInMemoryRealm"];
</code>
纯内存数据库在每次程序退出时不会保存数据。
但是,这不会妨碍到realm的其他功能,包括请求,关系和线程安全。
假如你需要灵活的数据读写但又不想永久储存,那么纯内存数据库对你来说一定大有裨益。
注意: 如果某个纯内存Realm实例没有被引用,所有的数据就会被释放。
强烈建议你在app中用强引用来钳制所有新建的纯内存Realm数据库。
关系
可以通过使用RLMObject 和 RLMArrayproperties互相联结。
假如说你预先定义了一个”人“模型(see above),我们再来创建一个”狗“模型。:
<code>
// Dog.h
@interface Dog : RLMObject
@property NSString *name;
@end
</code>
对一
对于多对一和一对一关系, 仅仅只需要定义一个RLMObject子类类型的property:
<code>
// Dog.h
@interface Dog : RLMObject...
// other property declarations
@property Person *owner;
@end
</code>
你可以像往常一样使用之:
<code>
Person *jim = [[Person alloc] init];
Dog *rex = [[Dog alloc] init];
rex.owner = jim;
</code>
当你使用RLMObject properties,你可以通过正常的property语法获取嵌套属性。
例如rex.owner.address.country会遍历这个对象图来获得你所想要的子类。
对多
你可以通过使用RLMArrayproperties来定义一个对多关系。
RLMArrays包含多个同类的RLMObjects,并且拥有一个和NSMutableArray非常相似的interface(接口)。
如果想要把”dogs“(多个”Dog“)属相添加到”Person“模型中来,我们必须定义一个RLMArray ————这可以通过在相应的模型(model)interface中添加一个宏(macro)来实现。
<code>
//Dog.h
@interface Dog : RLMObject...
// property declarations
@end
RLM_ARRAY_TYPE(Dog)
// Defines an RLMArraytype
</code>
然后你可以定义属性,类型为RLMArray。
<code>
// Person.h
@interface Person : RLMObject...
// other property declarations
@property RLMArray*dogs;
@end
</code>
我们又可以像往常一样读取和赋值啦。
<code>
// Jim is owner of Rex and all dogs named "Fido"
RLMArray *someDogs = [Dog objectsWhere:@"name contains 'Fido'"];
[jim.dogs addObjects:someDogs];
[jim.dogs addObject:rex];
</code>
通知
每当一次写事务完成Realm实例都会向其他线程上的实例发出通知,可以通过注册一个block来响应通知:
<code>
// Observe Realm Notifications
self.token = [realm addNotificationBlock:^(NSString *note, RLMRealm * realm) {
[myViewController updateUI];
}];
</code>
只要有任何的引用指向这个返回的notification token,它就会保持激活状态。
在这个注册更新的类里,你需要有一个强引用来钳制这个token, 因为一旦notification token被释放,通知也会自动解除注册。
具体内容:
[Realm addNotificationBlock:]
和[Realm removeNotificationBlock:]
。
后台操作
通过把大量的写入放入到一个大型事务中,Realm可以大大的提高大型数据读取的运行效率。
事务可以在后台通过GCD运行,这样可以避免阻塞主进程。
RLMRealm并不是线程安全的,所以你必须在每一个你需要读写的进程或者调度队列中添加RLMRealm实例。
这里有一个在后台队列中添加百万级数据的例子。
dispatch_async(queue, ^{
// Get realm and table instances for this thread
RLMRealm *realm = [RLMRealm defaultRealm];
// Break up the writing blocks into smaller portions
// by starting a new transaction
for (NSInteger idx1 = 0; idx1 < 1000; idx1++) {
[realm beginWriteTransaction];
// Add row via dictionary. Property order is ignored.
for (NSInteger idx2 = 0; idx2 < 1000; idx2++) {
[Person createInRealm:realm withObject:@{@"name":[self randomString],@"birthdate":[self randomDate]}];
}
// Commit the write transaction
// to make this data available to other threads
[realm commitWriteTransaction];
}
});
可以参考RLMRealm。
REST APIs<p>
Realm轻松的整合了REST API, 这使得Realm在几个方面胜过了无本地缓存的REST API:
Realm缓存数据使得你能提供离线体验,普通的REST API无法做到这一点——他们一定需要网络连接。
通过在本地存储你的整个数据集,你可以在本地进行查询,这能提供比普通REST API好很多的本地搜索体验。
可直接从Realm查询数据,不必等待服务器端复杂的API处理。
减轻服务器端负荷,只需要在更新和修改数据时进行必要的访问。
最佳操作
异步请求
网络请求和其他一些操作应该放到后台,以免影响交互效果。同理Realm数据库中大规模插入和修改应该在后台进行。你可以用通知来相应后台操作。
缓存数据库大于用户当下查询
我们建议你对可能使用的数据进行预处理并且存储到Realm中。 这么做可以让你在本地数据集中进行查询。
插入或更新
如果你的数据集有一个特有的标识符, 例如一个主键, 你可以用它来判定插入还是更新。只需要使用[RLMObject createOrUpdateInDefaultRealmWithObject:];
如果你从API得到响应, 这个method会从Realm中查询这个响应是否有记录。
如果在本地有记录, 就可以从响应中根据最新的数据进行更新。如果没有,就将该响应插入到Realm数据库中。
示例
以下是一个如何应用一个使用了REST API的Realm的示例。在这个示例里,我们将从foursquare API里获取一组JSON格式的数据,然后将它以Realm Objects的形式储存到默认realm数据库里。 如果你想参考类似示例的实际操作,请观看 video demo.
首先我们要创建一个默认Realm数据库的实例,用于存储数据以及从 API 获取数据。为了更简单易读,我们在这个例子里面运动了
[NSData initWithContentsOfURL]
.
// Call the API NSData *response = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString:@"https://api.foursquare.com/v2/venues/search?near=San%20Francisco&limit=50"]]; // Deserialize the response to JSON NSDictionary *json = [[NSJSONSerialization JSONObjectWithData:response options:kNilOptions error:&error] objectForKey:@"response"];
这条响应包含了JSON数组,形式类似于:
<code>
{
"venues": [{"id": "4c82f252d92ea09323185072",
"name": "Golden Gate Park",
"contact": {"phone": "4152522590"},
"location": {"lat": 37.773835608329,"lng": -122.41962432861,"postalCode": "94103","cc": "US","state": "California","country": "United States"}
}]
}
</code>
要想把JSON数据导入Realm中我们有很多办法,殊途同归。你可以读取 NSDictionary然后将其属性通过插入功能手动映射到一个RLMObject上。
为了演示效果,在这个示例里,我们将直接把 NSDictionary插入到Realm中,然后让Realm自动快速将其属性映射到RLMObject上。
为了确保示例能够成功,我们需要一个属性完全匹配JSON数据特点的RLMObject的框架。JSON数据特点如果得不到匹配,将在植入时自动被忽略。
以下RLMObject的定义是有效的:
// Contact.h
@interface Contact : RLMObject
@property NSString *phone;
@end
@implementation Contact
+ (NSString)primaryKey {
return @"phone";
}
@end
RLM_ARRAY_TYPE(Contact)
// Location.h
@interface Location : RLMObject
@property double lat; // latitude
@property double lng; // longitude
@property NSString *postalCode;
@property NSString *cc;
@property NSString *state;
@property NSString *country;
@end
@implementation Location
@end
RLM_ARRAY_TYPE(Location)
// Venue.h
@interface Venue : RLMObject
@property NSString *id;
@property NSString *name;
@property Contact *contact;
@property Location *location;
@end
@implementation Venue
+ (NSString)primaryKey {
return @"id";
}
@end
RLM_ARRAY_TYPE(Venue)
因为结果集是以数组的形式给我们的,我们要呼叫
[Venue createInDefaultRealmWithObject:]
来为每个元素创建一个对象. 这里会创建 Venue 和一个JSON格式的子对象,并将这些新建的对象加入到默认realm数据库中:
//Extract the array of venues from the response
NSArray *venues = json[@"venues"];
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
// Save one Venue object (and dependents) for each element of the array
for (NSDictionary *venue in venues) {
[Venue createOrUpdateInDefaultRealmWithObject:venue];
}
[realm commitWriteTransaction];
数据库迁移
当你和数据库打交道的时候,时不时的你需要改变数据模型(model),但因为Realm中得数据模型被定义为标准的Objective-C interfaces,要改变模型,就像改变其他Objective-C interface一样轻而易举。举个例子,假如我们有如下的interface, 叫“Person.h”:
@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
@property int age;
@end
我们想要更新数据模型,因为我们要添加一个“全名”(fullname)属性, 而不是用分开的“姓”+“名”。要达到这样的目的,我们只需要改变对象的interface,如下:
@interface Person : RLMObject
@property NSString *fullName;
@property int age;
@end
在这个时候如果你保存了数据,那么Realm就会注意到代码和硬盘数据不匹配。 每当这时,你必须对数据构架进行迁移,否则就会有错误抛出。
进行迁移
你可以通过呼叫[RLMRealm setSchemaVersion:withMigrationBlock:]
自定义数据迁移以及相应的构架版本。
你的数据迁移模块将会为你提供相应地逻辑,用来更新数据构架。呼叫[RLMRealm setSchemaVersion:withMigrationBlock:]
之后, 任何需要迁移的Realm数据库都会自动使用指定的迁移模块并且更新到相应地版本。
例如,假设我们想要把之前‘Person’的子类迁移,如下所示是最简化的数据迁移组:
// Inside your [AppDelegate didFinishLaunchingWithOptions:]
// Notice setSchemaVersion is set to 1, this is always set manually. It must be
// higher than the previous version (oldSchemaVersion) or an RLMException is thrown
[RLMRealm setSchemaVersion:1 forRealmAtPath:[RLMRealm defaultRealmPath] withMigrationBlock:^(RLMMigration *migration, NSUInteger oldSchemaVersion) {
// We haven’t migrated anything yet, so oldSchemaVersion == 0
if (oldSchemaVersion < 1) {
// Nothing to do!
// Realm will automatically detect new properties and removed properties
// And will update the schema on disk automatically
}
}];
// now that we have called `setSchemaVersion:withMigrationBlock:`, opening an outdated
// Realm will automatically perform the migration and opening the Realm will succeed
[RLMRealm defaultRealm];
我们所需要做的就是用一个空模块更新版本,表明这个构架已经被Realm自动更新了。
虽然这是系统能接受的最简化的迁移,我们应当用有意义的代码来填充这些新的属性(这里就是“fullname”)。在数据迁移模块中,我们可以呼叫[RLMMigration enumerateObjects:block:] 来列举某种格式的每一个Realm文件,并执行必要的迁移判定:
// Inside your [AppDelegate didFinishLaunchingWithOptions:]
[RLMRealm setSchemaVersion:1 forRealmAtPath:[RLMRealm defaultRealmPath] withMigrationBlock:^(RLMMigration *migration, NSUInteger oldSchemaVersion) {
// We haven’t migrated anything yet, so oldSchemaVersion == 0
if (oldSchemaVersion < 1) {
// The enumerateObjects:block: method iterates
// over every 'Person' object stored in the Realm file
[migration enumerateObjects:Person.className block:^(RLMObject *oldObject, RLMObject *newObject) {
// combine name fields into a single field
newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@",
oldObject[@"firstName"],
oldObject[@"lastName"]];
}];
}
}];
一旦迁移成功结束,Realm和其所有文件即可被你的app正常存取。
添加更多的版本
假如说现在我们有两个之前版本的Person类:
// v0
@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
@property int age;
@end
// v1
@interface Person : RLMObject
@property NSString *fullName; // new property
@property int age;
@end
// v2
@interface Person : RLMObject
@property NSString *fullName;
@property NSString *email; // new property
@property int age;
@end
我们的迁移模块里面的逻辑大致如下:
[RLMRealm setSchemaVersion:2 forRealmAtPath:[RLMRealm defaultRealmPath]
withMigrationBlock:^(RLMMigration *migration,
NSUInteger oldSchemaVersion) {
// The enumerateObjects:block: method iterates
// over every 'Person' object stored in the Realm file
[migration enumerateObjects:Person.className
block:^(RLMObject *oldObject, RLMObject *newObject) {
// Add the 'fullName' property only to Realms with a schema version of 0
if (oldSchemaVersion < 1) {
newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@",
oldObject[@"firstName"],
oldObject[@"lastName"]];
}
// Add the 'email' property to Realms with a schema version of 0 or 1
if (oldSchemaVersion < 2) {
newObject[@"email"] = @"";
}
}];
}];
// now that we have called `setSchemaVersion:withMigrationBlock:`, opening an outdated
// Realm will automatically perform the migration and opening the Realm will succeed
[RLMRealm defaultRealm];
想要了解更多关于数据库的框架迁移, 参见migration sample app.
Linear Migrations
假如说,我们的app有两个用户: JP和Tim. JP经常更新app,但Tim却经常跳过。 所以JP可能下载过这个app的每一个版本,并且一步一步的跟着更新构架:他下载第一次更新,从v0到v1, 第二次更新从v1到v2,以此类推,井然有序。相反,Tim很有可能直接从v0跳到了v2。 所以,你应该使用非嵌套的 if (oldSchemaVersion < X)结构来构造你的数据库迁移模块,以确保不管他们是在使用哪个版本的构架,都能看见所有的更新。
当你的用户不按规则出牌,跳过有些更新版本的时候,另一种情况也会发生。 假如你在v2里删掉了一个“email”属性,然后在v3里又把它重新引进了。假如有个用户从v1直接跳到v3,那Realm不会自动检测到v2的这个删除操作因为存储的数据构架和代码中的构架吻合。这会导致Tim的Person对象有一个v3的email property,但里面的内容却是v1的。这个看起来没什么大问题,但是假如两者的内部存储类型不同(比如说: 从ISO email representation 变成了自定义),那麻烦就大了。为了避免这种不必要的麻烦,我们推荐你在if (oldSchemaVersion < 3)中,nil out所有的email property。
下一步
你可以看一下我们的我们给出的示例。看看在app中应该如何使用realm(我们已经有越来越多的样本了!)
做一个愉快地码农!你也总是可以在realm-cocoa上实时的和其他开发者聊天。
当前的限制
realm现在还是beta版本。我们还在为1.0的发布一直不断的添加新特性,修复bug。我们整理了一些普遍存在的限制
如果你想要看到完整地issue列表, 参见GitHub issues。
Realm CocoaPods 不支持Swift 项目开发
CocoaPods 暂时还不支持Swift 项目开发(戳这个GitHub issue #2222). 想要在一个Swift 项目中使用Realm,请参见上面的步骤.
暂时不支持通知细节
虽然要在realm发生变化的时候可以接到通知 (参见 通知), 但现在我们还不能从notification里面得知什么东西被添加/删减/移动/更新了。 我们会尽快完善这个功能的。
NSDate在秒的地方被截断
一个包含非整秒数的NSDate在存入realm的时候,会在秒的地方被截断。我们正在修复这个问题。 可参考 GitHub issue #875。同时,你可以无损存储NSTimeInterval格式。
Realm对象的Setters & Getters不能被重写
因为Realm重写了setters和getters, 所以你不可以在你的对象上再重写。一个简单的替代方法就是:创建一个新的realm-ignored属性(它的accessors可以被重写, 并且可以呼叫其他的getter和setter)。
不支持 KVO
Realm不支持KVO, 但它有自己的通知机制(see 通知).
Realm文件不能被两个进程同时访问
尽管Realm文件可以在多个线程中被同时访问, 它们每次只能被一个进程访问。这对iOS 8和OSX应用有影响。不同的进程应该复制或者新建Realm文件。 敬请期待多进程支持。
<p>
FAQ
realm的支持库有多大?
一旦你的app编译完成, realm的支持库应该只有1 MB左右。 我们发布的那个可能有点大(iOS ~37MB, OSX ~2.4MB), 那是因为它们还包含了对其他构架的支持(ARM, ARM64,模拟器的是X86)和一些编译符号。 这些都会在你编译app的时候被Xcode自动清理掉。
我应该在正式产品中使用realm吗?
自2012年起, realm就已经开始被用于正式的商业产品中了。
正如你预期,我们的objective-c & Swift API 会随着社区的反馈不断的完善和进化。 所以,你也应该期待realm带给你更多的新特性和版本修复。
我该如何保护Realm里面的数据?
Realm 有几种加密方法, 各有千秋。参考这个Github评论。Realm将在未来支持跨平台加密。
我要付realm的使用费用吗?
不要, Realm的彻底免费的, 哪怕你用于商业软件。
你们计划怎么赚钱?
其实,我们靠着我们的技术,已经开始赚钱啦!这些钱来自于我们销售企业级产品的利润。如果你想要得到比普通发行版本或者realm-cocoa更多的支持, 我们很高兴和你发邮件聊聊。 我们一直致力于开发开源的(Apache 2.0),免费的realm-cocoa。
我看到你们在代码里有“tightdb”或者“core”, 那是个啥?
TightDB是我们的C++存储引擎的旧名。core 现在还没有开源但我们的确想这样做(依旧使用Apache2.0)假如我们有时间来进行清理,重命名等工作。同时,它的二进制发行版在Realm Core(tightDB)Binary License里面可以找到。
订阅我们定期发布的community newsletter。
里面有一些非常有用的提示和其他的用例。当有新的Realm博客或者教程出现,邮件也会通知你。StackOverflow: 查找之前的有#realm标签的问答, — 或者,开一个新的。
推特: 联系@realm 或者用#realm标签。
Email: docs/cocoa/latest realm-cocoa@googlegroups.com.
官网链接:https://realm.io