天天看点

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont

<code>Realm</code>是一个移动端的数据库,<code>Realm</code>是<code>SQLite</code>和<code>CoreData</code>的替代者。它可以节省你成千上万行代码和数周的工作,并且让你精巧的制作出令人惊叹的用户体验。

文档版本 0.93.2

需求:

iOS7以上 OS X10.9以上 WatchKit.

Xcode 6 及以上 必需.

支持Objective-C Swift 1.2 Swift 2.0

静态库安装 (Objective-C &amp; Swift)

1.下载最新的<code>Realm</code>发行版本并在本地解压。

2.从 <code>ios/static/</code>目录里,把<code>Realm.framework</code>文件拖动到你的Xcode开发项目里的<code>File Navigator</code> 中。 确保<code>Copy items into destination group’s folder</code>已经被选中,按Finish。

3.在<code>Xcode file explorer</code>中选中你要的开发项目. 选择<code>target</code>,点击<code>Build Phases</code>选项. 在<code>Link Binary with Libraries</code>里按+,添加<code>libc++.dylib</code>。

4.如果使用<code>Realm + Swift</code>,拖动<code>Swift/RLMSupport.swift</code>到你的<code>Xcode project</code>的<code>File Navigator</code>中,点选<code>Copy items if needed</code>。

5.如果在OSX项目中使用Realm,点击左上角的 + ,选择<code>New Copy Files Phase</code>,将其重命名为<code>Copy Frameworks</code>, 将Destination设置为Frameworks,并且添加<code>Realm.framework</code>。

动态库安装 (Objective-C &amp; Swift)

2.来到Xcode工程的<code>General</code>设置界面,从 <code>ios/dynamic/</code>或者<code>osx/</code> 目录里,把<code>Realm.framework</code>文件拖动到<code>Embedded Binaries</code>里面。 确保<code>Copy items into destination group’s folder</code>已经被选中,按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)

如果你使用<code>CocoaPods…</code>

1.把<code>pod "Realm"</code>添加到你的Podfile中。

2.在命令行中执行<code>pod install</code>。

3.将<code>CocoaPods</code>生成的<code>.xcworkspace</code>运用到你的开发项目中即可。

你也可以自行手动安装:打开<code>release zip</code>中的<code>plugin/RealmPlugin.xcodeproj</code>, 点击编译<code>build</code>。 重启Xcode生效。

如果你使用Xcode新建文件<code>File &gt; New &gt; File… — or ⌘ N</code>,可以看到有一个新建Realm模型<code>create a new realm model</code>的选项。

我们另外提供了一个独立的数据库管理工具,用来查看和编辑realm数据库<code>.realm</code>。

你可以从<code>browser/</code>的<code>release zip</code>目录下找到它。

使用菜单中的工具<code>tool</code>&gt;生成演示数据库<code>generate demo database</code>, 你可以生成一个测试数据库(当然里面的数据是样本数据).

你可以在<code>examples/</code>路径里面找到一个文件,叫做<code>release zip</code>。 里面包含了<code>Objective-C</code>,<code>Swift</code>和<code>RubyMotion</code>的示例程序。 它们演示了<code>Realm</code>得很多功能和特性,例如数据库迁移,如何与<code>UITableViewController’s</code>一起使用,加密等等。

Realm的数据模型是用传统的Objective-C 接口<code>interface</code>和<code>@properties</code>定义的。 就只要定义<code>RLMObject</code>的一个子类<code>subclass</code>或者一个现成的模型类,你就能轻松创建一个<code>Realm</code>的数据模型对象<code>data model object</code>。<code>Realm</code>模型对象和其他的<code>objective-c</code>的功能很相似–你可以给它们添加你自己的方法<code>method</code>和协议<code>protocol</code>然后和其他的对象一样使用。

唯一的限制就是从它们被创建开始,只能在一个线程中被使用。

如果你已经安装了我们的<code>Xcode</code>插件 那么,在<code>New File</code>对话框中会有一个很漂亮的样板,你可以用它来创建<code>interface</code>和<code>implementation</code>文件。

你只需要为对象列表添加目标类型的属性<code>property</code>或者<code>RLMArray</code>的,就可以创建数据库关联和嵌套数据结构

<code></code>

RLMObject的相关细节.

属性<code>property</code>种类

Realm支持以下的属性<code>property</code>种类: <code>BOOL</code>,<code>bool</code>, <code>int</code>,<code>NSInteger</code>,<code>long</code>,<code>float</code>,<code>double</code>,<code>CGFloat</code>,<code>NSString</code>,<code>NSDate</code> 和<code>NSData</code>。

你可以使用<code>RLMArray&lt;NSObject&gt;</code> 和<code>RLMObject</code>来模拟对一或对多的关系

<code>Realm</code>也支持<code>RLMObject</code>继承。

属性<code>property</code>特性

请注意<code>Realm</code>忽略了<code>objective-c</code>的<code>property attributes</code>,像<code>nonatomic</code>,<code>atomic</code>,<code>strong</code>,<code>copy</code>,<code>weak</code>等等。

所以,为了避免误解,我们推荐你在写入模型的时候不要使用任何的<code>property attributes</code>。但是,假如你设置了,这些<code>attributes</code>会一直生效直到<code>RLMObject</code>被写入<code>realm</code>数据库。

无论<code>RLMObject</code>在或不在<code>realm</code>中,你为<code>getter</code>和<code>setter</code>自定义的名字都能正常工作。

数据模型定制

几个存在的类方法进一步指定模型信息:

<code>+attributesForProperty:</code>可以被重写来来提供特定属性<code>property</code>的属性值<code>attrbutes</code>例如某个属性值要添加索引。

<code>+defaultPropertyValues</code>可以被重写,用以为新建的对象提供默认值。

<code>+primaryKey</code>可以被重写来设置模型的主键。定义主键可以提高效率并且确保唯一性。

<code>ignoredProperties</code>可以被重写来防止<code>Realm</code>存储模型属性。

对对象的所有更改(添加,修改 和删除)都必须通过写入事务完成。

<code>Realm</code>的对象可以被实例化并且被单独使用,和其他常规对象无异。

如果你想要在多个线程中共享或者永久保存以重复使用对象,你必须将其存储到Realm数据库中——这个操作必须在写事务中完成。 你可以参照如下代码添加一个对象:

创建一个对象

<code>Person *author = [[Person alloc] init];author.name = @"David Foster Wallace";</code>

获取一个默认realm对象

<code>RealmRLMRealm *realm = [RLMRealm defaultRealm];</code>

你只须这么做一次(单线程操作) Add to Realm with transaction

等到你把这个对象添加到realm数据库里面之后, 你可以在多个线程里面共享之。

并且从现在开始,你所做的每一次更改(必须在一个写事务中完成)也会被永久储存。

等到写事务完成,这个更改将对所有共享这个Realm数据库的线程可见。

需要注意的是,写入操作会相互阻塞,而且其相对应的进程也会受到影响。

这和其他的永久数据存储解决方案是一样的,所以我们建议你使用常用的,也是最有效的方案, 将所有写入放到一个单独的进程中。

还要注意的是,因为realm的MVCC结构, 读取并不会 因为一个进行中的写事务而受到影响。

所有的数据抓取都很简单,并且直到获得数据之后才创建副本。

关于使用RLMResults的小贴士:

Realm的对象查询返回一个RLMResults对象。它包含了一系列的RLMObject。

RLMResults有一个与NSArray很相似的interface(接口)并且对象可以通过索引(index)下标获取。

但不同于NSArrays的是,RLMResult是归类的——它只能容纳一种RLMObjects类型。

根据种类获取对象从realm中获取对象的最基本方法就是 [RLMObject allObjects], 它返回一个RLMResults,里面是查询的子类的所有RLMObject实例。

谓词/条件查询

如果你对 NSPredicate很熟悉的话, 那么你就已经知道怎么在realm里面查询了。

<code>RLMObjects</code>, <code>RLMRealm</code>, <code>RLMArray</code>和<code>RLMResults</code>都提供很好的<code>methods</code>来查询特定的<code>RLMObjects</code>:

你只需要传递相应地<code>NSPredicate</code>实例,谓词字符串,谓词格式字符串,就可以获取你想要的<code>RLMObjects</code>实例。就和<code>NSObject</code>一样的。

举个例子,下面的代码就是对上面的拓展。 通过调用<code>[RLMObject objectsWhere:]</code>,获得了默认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)支持 ==, &lt;=, &lt;, &gt;=, &gt;, !=, 和 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”进行排序,排序的条件是名字的字母表升序。:

了解更多:[RLMObject objectsWhere:]和[RLMResults sortedResultsUsingProperty:ascending:]。链式查询

Realm查询引擎的一个独特属性就是它能够进行简单快捷的链式查询, 而不需要像传统数据库一样的麻烦。举个例子来说,假如你要所有黄褐色的小狗的结果序列,然后从中查找名字开头是“B“的小狗。 你可以发送如下的请求。

默认的realm数据库

你可能已经注意到了我们总是通过[RLMRealm defaultRealm]来获得realm数据库。

这个method返回一个RLMRealm对象,指向一个叫做”default.realm“的文件。

这个文件在你的app的Documents文件夹里面。

其他的realm数据库

有的时候拥有多个分离的数据库是非常有必要的。

例如,如果你需要绑定数据到一个App,你可以打开另一个只读的Realm数据库,参考See [RLMRealm realmWithPath:] 和 [RLMRealm realmWithPath:readOnly:error:]

请注意传递到[RLMRealm realmWithPath:]的路径必须是有写入权限的。

存储可写Realm数据库的最常用路径就是iOS的“Documents”和OSX的“Application Support”。

跨线程使用数据库

在不同的线程间使用同一个Realm数据库,你必须呼叫[RLMRealm defaultRealm], [RLMRealm realmWithPath:] 或者 [RLMRealm realmWithPath:readOnly:error:]以得到个线程相对应的realm对象。

只要你路径一致,所有的RLMRealm对象指向的文件就一致。

我们还不支持跨线程共享RLMRealm对象。

纯内存数据库

正常的Realm数据库是存储在硬盘上的, 但你也可以通过使用

[RLMRealm inMemoryRealmWithIdentifier:]来创建一个纯内存数据库。

纯内存数据库在每次程序退出时不会保存数据。

但是,这不会妨碍到realm的其他功能,包括请求,关系和线程安全。

假如你需要灵活的数据读写但又不想永久储存,那么纯内存数据库对你来说一定大有裨益。

注意: 如果某个纯内存Realm实例没有被引用,所有的数据就会被释放。

强烈建议你在app中用强引用来钳制所有新建的纯内存Realm数据库。

可以通过使用RLMObject 和 RLMArrayproperties互相联结。

假如说你预先定义了一个”人“模型(see above),我们再来创建一个”狗“模型。:

对一

对于多对一和一对一关系, 仅仅只需要定义一个RLMObject子类类型的property:

你可以像往常一样使用之:

当你使用RLMObject properties,你可以通过正常的property语法获取嵌套属性。

例如rex.owner.address.country会遍历这个对象图来获得你所想要的子类。对多

你可以通过使用RLMArrayproperties来定义一个对多关系。

RLMArrays包含多个同类的RLMObjects,并且拥有一个和NSMutableArray非常相似的interface(接口)。

如果想要把”dogs“(多个”Dog“)属相添加到”Person“模型中来,我们必须定义一个RLMArray ————这可以通过在相应的模型(model)interface中添加一个宏(macro)来实现。

然后你可以定义属性,类型为RLMArray。

我们又可以像往常一样读取和赋值啦。

每当一次写事务完成Realm实例都会向其他线程上的实例发出通知,可以通过注册一个block来响应通知:

只要有任何的引用指向这个返回的notification token,它就会保持激活状态。

在这个注册更新的类里,你需要有一个强引用来钳制这个token, 因为一旦notification token被释放,通知也会自动解除注册。

具体内容:[Realm addNotificationBlock:] 和 [Realm removeNotificationBlock:]。

通过把大量的写入放入到一个大型事务中,Realm可以大大的提高大型数据读取的运行效率。

事务可以在后台通过GCD运行,这样可以避免阻塞主进程。

RLMRealm并不是线程安全的,所以你必须在每一个你需要读写的进程或者调度队列中添加RLMRealm实例。

这里有一个在后台队列中添加百万级数据的例子。

可以参考RLMRealm。

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

这条响应包含了JSON数组,形式类似于:

要想把JSON数据导入Realm中我们有很多办法,殊途同归。你可以读取 NSDictionary然后将其属性通过插入功能手动映射到一个RLMObject上。

为了演示效果,在这个示例里,我们将直接把 NSDictionary插入到Realm中,然后让Realm自动快速将其属性映射到RLMObject上。

为了确保示例能够成功,我们需要一个属性完全匹配JSON数据特点的RLMObject的框架。JSON数据特点如果得不到匹配,将在植入时自动被忽略。

以下RLMObject的定义是有效的:

因为结果集是以数组的形式给我们的,我们要呼叫 [Venue createInDefaultRealmWithObject:] 来为每个元素创建一个对象. 这里会创建 Venue 和一个JSON格式的子对象,并将这些新建的对象加入到默认realm数据库中:

数据库迁移

当你和数据库打交道的时候,时不时的你需要改变数据模型(model),但因为Realm中得数据模型被定义为标准的Objective-C interfaces,要改变模型,就像改变其他Objective-C interface一样轻而易举。举个例子,假如我们有如下的interface, 叫“Person.h”:

我们想要更新数据模型,因为我们要添加一个“全名”(fullname)属性, 而不是用分开的“姓”+“名”。要达到这样的目的,我们只需要改变对象的interface,如下:

在这个时候如果你保存了数据,那么Realm就会注意到代码和硬盘数据不匹配。 每当这时,你必须对数据构架进行迁移,否则就会有错误抛出。

进行迁移

你可以通过呼叫[RLMRealm setSchemaVersion:withMigrationBlock:]自定义数据迁移以及相应的构架版本。你的数据迁移模块将会为你提供相应地逻辑,用来更新数据构架。呼叫[RLMRealm setSchemaVersion:withMigrationBlock:]之后, 任何需要迁移的Realm数据库都会自动使用指定的迁移模块并且更新到相应地版本。

例如,假设我们想要把之前‘Person’的子类迁移,如下所示是最简化的数据迁移组:

我们所需要做的就是用一个空模块更新版本,表明这个构架已经被Realm自动更新了。

虽然这是系统能接受的最简化的迁移,我们应当用有意义的代码来填充这些新的属性(这里就是“fullname”)。在数据迁移模块中,我们可以呼叫[RLMMigration enumerateObjects:block:] 来列举某种格式的每一个Realm文件,并执行必要的迁移判定:

一旦迁移成功结束,Realm和其所有文件即可被你的app正常存取。

添加更多的版本

假如说现在我们有两个之前版本的Person类:

我们的迁移模块里面的逻辑大致如下:

想要了解更多关于数据库的框架迁移, 参见migration sample app.

Linear Migrations

假如说,我们的app有两个用户: JP和Tim. JP经常更新app,但Tim却经常跳过。 所以JP可能下载过这个app的每一个版本,并且一步一步的跟着更新构架:他下载第一次更新,从v0到v1, 第二次更新从v1到v2,以此类推,井然有序。相反,Tim很有可能直接从v0跳到了v2。 所以,你应该使用非嵌套的 if (oldSchemaVersion &lt; X)结构来构造你的数据库迁移模块,以确保不管他们是在使用哪个版本的构架,都能看见所有的更新。

当你的用户不按规则出牌,跳过有些更新版本的时候,另一种情况也会发生。 假如你在v2里删掉了一个“email”属性,然后在v3里又把它重新引进了。假如有个用户从v1直接跳到v3,那Realm不会自动检测到v2的这个删除操作因为存储的数据构架和代码中的构架吻合。这会导致Tim的Person对象有一个v3的email property,但里面的内容却是v1的。这个看起来没什么大问题,但是假如两者的内部存储类型不同(比如说: 从ISO email representation 变成了自定义),那麻烦就大了。为了避免这种不必要的麻烦,我们推荐你在if (oldSchemaVersion &lt; 3)中,nil out所有的email property。

下一步

你可以看一下我们的我们给出的示例。看看在app中应该如何使用realm(我们已经有越来越多的样本了!)

做一个愉快地码农!你也总是可以在realm-cocoa上实时的和其他开发者聊天。

realm现在还是beta版本。我们还在为1.0的发布一直不断的添加新特性,修复bug。我们整理了一些普遍存在的限制

Realm CocoaPods 不支持Swift 项目开发

CocoaPods 暂时还不支持Swift 项目开发(戳这个GitHub issue #2222). 想要在一个Swift 项目中使用Realm,请参见上面的步骤.

暂时不支持通知细节

虽然要在realm发生变化的时候可以接到通知 (参见 通知), 但现在我们还不能从notification里面得知什么东西被添加/删减/移动/更新了。 我们会尽快完善这个功能的。

NSDate在秒的地方被截断

一个包含非整秒数的NSDate在存入realm的时候,会在秒的地方被截断。我们正在修复这个问题。 可参考 GitHub issue #875。同时,你可以无损存储NSTimeInterval格式。

Realm对象的Setters &amp; Getters不能被重写

因为Realm重写了setters和getters, 所以你不可以在你的对象上再重写。一个简单的替代方法就是:创建一个新的realm-ignored属性(它的accessors可以被重写, 并且可以呼叫其他的getter和setter)。

不支持 KVO

Realm不支持KVO, 但它有自己的通知机制(see 通知).

Realm文件不能被两个进程同时访问

尽管Realm文件可以在多个线程中被同时访问, 它们每次只能被一个进程访问。这对iOS 8和OSX应用有影响。不同的进程应该复制或者新建Realm文件。 敬请期待多进程支持。

realm的支持库有多大?

一旦你的app编译完成, realm的支持库应该只有1 MB左右。 我们发布的那个可能有点大(iOS ~37MB, OSX ~2.4MB), 那是因为它们还包含了对其他构架的支持(ARM, ARM64,模拟器的是X86)和一些编译符号。 这些都会在你编译app的时候被Xcode自动清理掉。

我应该在正式产品中使用realm吗?

自2012年起, realm就已经开始被用于正式的商业产品中了。

正如你预期,我们的objective-c &amp; 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 [email protected].

<a href="https://realm.io"></a>

&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt; &lt;html&gt;&lt;head&gt;&lt;meta http-equiv="Cont