Swift - Realm数据库的使用详解(附样例)
1,什么是Realm
Realm 于2014 年7月发布,是一个跨平台的移动数据库引擎,专门为移动应用的数据持久化而生。其目的是要取代 Core Data 和 SQLite。
2,关于Realm,你要知道下面几点:
(1)使用简单,大部分常用的功能(比如插入、查询等)都可以用一行简单的代码轻松完成,学习成本低。
(2)Realm 不是基于 Core Data,也不是基于 SQLite 封装构建的。它有自己的数据库存储引擎。
(3)Realm 具有良好的跨平台特性,可以在 iOS 和 Android 平台上共同使用。代码可以使用 Swift 、 Objective-C 以及 Java 语言来编写。
(4)Realm 还提供了一个轻量级的数据库查看工具(Realm Browser)。你也可以用它进行一些简单的编辑操作(比如插入和删除操作)
3,支持的类型
(1)Realm 支持以下的属性类型:Bool、Int8、Int16、Int32、Int64、Double、Float、String、Date(精度到秒)以及Data.
(2)也可以使用 List<object> 和 Object 来建立诸如一对多、一对一之类的关系模型,此外 Object 的子类也支持此功能。
4,Realm的安装配置
(1)先去 Realm 的官网去下载最新框架:http://static.realm.io/downloads/swift/latest。(或者使用cocoapods下载RealmSwift第三方。最新的使用方法案例官网地址:https://realm.io/cn/docs/swift/latest/)
(2)拖拽 RealmSwift.framework 和 Realm.framework 文件到”Embedded
Binaries”选项中。选中 Copy items if needed 并点击 Finish
5,将数据插入到数据库中
下面代码判断默认数据库中是否有数据,如果没有的话将几个自定义对像插入到数据库中。
(1)这里以个人消费记录为例,我们先定义消费类别类,和具体消费记录类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
import Foundation
import RealmSwift
//消费类型
class ConsumeType : Object {
//类型名
@objc dynamic var name
= ""
}
//消费条目
class ConsumeItem : Object {
//条目名
@objc dynamic var name
= ""
//金额
@objc dynamic var cost
= 0.00
//时间
@objc dynamic var date
= Date ()
//所属消费类别
@objc dynamic var type: ConsumeType ?
}
|
(2)判断数据库记录是否为空,空的话则插入数据库(这里以默认数据库为例)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
import UIKit
import RealmSwift
class ViewController : UIViewController {
override func viewDidLoad()
{
super .viewDidLoad()
//使用默认的数据库
let realm
= try! Realm ()
//查询所有的消费记录
let items
= realm.objects( ConsumeItem . self )
//已经有记录的话就不插入了
if items.count>0
{
return
}
//创建两个消费类型
let type1
= ConsumeType ()
type1.name
= "购物"
let type2
= ConsumeType ()
type2.name
= "娱乐"
//创建三个消费记录
let item1
= ConsumeItem (value:
[ "买一台电脑" ,5999.00, Date (),type1]) //可使用数组创建
let item2
= ConsumeItem ()
item2.name
= "看一场电影"
item2.cost
= 30.00
item2.date
= Date (timeIntervalSinceNow:
-36000)
item2.type
= type2
let item3
= ConsumeItem ()
item3.name
= "买一包泡面"
item3.cost
= 2.50
item3.date
= Date (timeIntervalSinceNow:
-72000)
item3.type
= type1
//
数据持久化操作(类型记录也会自动添加的)
try!
realm.write {
realm.add(item1)
realm.add(item2)
realm.add(item3)
}
//打印出数据库地址
print (realm.configuration.fileURL
?? "" )
}
override func didReceiveMemoryWarning()
{
super .didReceiveMemoryWarning()
}
}
|
7,使用Realm Browser查看数据库
(1)默认数据库是应用的 Documents 文件夹下的一个名为“default.realm”。
1
2
|
//打印出数据库地址
print (realm.configuration.fileURL
?? "" )
|
(2)使用 Realm Browser 工具可以很方便的对.realm数据库进行读取和编辑(在 App Store 中搜索 Realm
Browser 即可下载)。
可以看到,上面的几个对象已经成功的插入到数据库中来。
8,从数据库中读取记录并显示到表格中来
(1)通过查询操作,Realm 将会返回包含 Object 集合的 Results 实例。Results 的表现和 Array 十分相似,并且包含在 Results 中的对象能够通过索引下标进行访问。 (2)所有的查询(包括查询和属性访问)在 Realm 中都是延迟加载的,只有当属性被访问时,才能够读取相应的数据。
(3)查询结果并不是数据的拷贝:修改查询结果(在写入事务中)会直接修改硬盘上的数据。
下面我们把库里的数据加载出来,并通过表格显示出来。
效果图如下:
代码如下:
9,查询前N条数据
Realm无法直接限制查询数量。所以我们如果想要查出部分数据(比如前5条记录),也是全部查出来后在结果集中捞取。
10,支持断言查询(Predicate),这样可以通过条件查询特定数据
同时可以使用链式查询数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
import UIKit
import RealmSwift
class ViewController : UIViewController , UITableViewDelegate , UITableViewDataSource {
@IBOutlet weak var tableView: UITableView !
var dformatter
= DateFormatter ()
//保存从数据库中查询出来的结果集
var consumeItems: Results < ConsumeItem >?
override func viewDidLoad()
{
super .viewDidLoad()
self .dformatter.dateFormat
= "MM月dd日
HH:mm"
self .tableView!.delegate
= self
self .tableView!.dataSource
= self
//创建一个重用的单元格
self .tableView!.register( UITableViewCell . self ,
forCellReuseIdentifier: "MyCell" )
//使用默认的数据库
let realm
= try! Realm ()
//查询所有的消费记录
consumeItems
= realm.objects( ConsumeItem . self )
}
//在本例中,只有一个分区
func numberOfSections( in tableView: UITableView )
-> Int {
return 1;
}
//返回表格行数(也就是返回控件数)
func tableView(_
tableView: UITableView ,
numberOfRowsInSection section: Int )
-> Int {
return self .consumeItems!.count
}
//创建各单元显示内容(创建参数indexPath指定的单元)
func tableView(_
tableView: UITableView ,
cellForRowAt indexPath: IndexPath )
-> UITableViewCell {
//同一形式的单元格重复使用,在声明时已注册
let cell
= UITableViewCell (style:
.value1, reuseIdentifier: "MyCell" )
let item
= self .consumeItems![indexPath.row]
cell.textLabel?.text
= item.name + "
¥" + String (format: "%.1f" ,
item.cost)
cell.detailTextLabel?.text
= self .dformatter.string(from:
item.date)
return cell
}
override func didReceiveMemoryWarning()
{
super .didReceiveMemoryWarning()
}
}
|
9,查询前N条数据
Realm无法直接限制查询数量。所以我们如果想要查出部分数据(比如前5条记录),也是全部查出来后在结果集中捞取。
1
2
3
4
5
6
|
//查询并取出前5条数据
let dogs
= try! Realm ().objects( Dog . self )
for i in 0..<5
{
let dog
= dogs[i]
//
...
}
|
Realm为何无法限制查询数量?
通常查询数据库数据时,我们可以在sql语句中添加一些限制语句(比如rownum,limit,top等)来限制返回的结果集的行数。
但我们使用Realm会发现,它没有这种分页功能,感觉不管查什么都是把所有的结果都捞出来。比如我们只要User表的前10条数据,那么做法是先查询出所有的User数据,再从结果集中取出前10条数据。
有人可能会担心,如果数据库中数据非常多,那每次都这么查不会影响性能吗?
其实大可放心,由于Realm都是延迟加载的,只有当属性被访问时,才能够读取相应的数据。不像通常数据库,查询后,查询结果是从数据库拷贝一份出来放在内存中的。而Realm的查询结果应该说是数据库数据的引用,就算你查出来,如果不用也不会占用什么内存。
通常查询数据库数据时,我们可以在sql语句中添加一些限制语句(比如rownum,limit,top等)来限制返回的结果集的行数。
但我们使用Realm会发现,它没有这种分页功能,感觉不管查什么都是把所有的结果都捞出来。比如我们只要User表的前10条数据,那么做法是先查询出所有的User数据,再从结果集中取出前10条数据。
有人可能会担心,如果数据库中数据非常多,那每次都这么查不会影响性能吗?
其实大可放心,由于Realm都是延迟加载的,只有当属性被访问时,才能够读取相应的数据。不像通常数据库,查询后,查询结果是从数据库拷贝一份出来放在内存中的。而Realm的查询结果应该说是数据库数据的引用,就算你查出来,如果不用也不会占用什么内存。
10,支持断言查询(Predicate),这样可以通过条件查询特定数据
同时可以使用链式查询数据。
1
2
3
4
5
6
7
8
9
|
//查询花费超过10元的消费记录(使用断言字符串查询)
consumeItems
= realm.objects( ConsumeItem . self ). filter ( "cost
> 10" )
//查询花费超过10元的购物记录(使用
NSPredicate 查询)
let predicate
= NSPredicate (format: "type.name
= '购物' AND cost > 10" )
consumeItems
= realm.objects( ConsumeItem . self ). filter (predicate)
//使用链式查询
consumeItems
= realm.objects( ConsumeItem . self ). filter ( "cost
> 10" ). filter ( "type.name
= '购物'" )
|
支持的断言:
- 比较操作数(comparison operand)可以是属性名称或者某个常量,但至少有一个操作数必须是属性名称;
- 比较操作符 ==、<=、<、>=、>、!=, 以及 BETWEEN 支持 int、long、long long、float、double 以及 NSDate 属性类型的比较,比如说 age == 45;
- 相等比较 ==以及!=,比如说Results<Employee>().filter("company == %@", company)
- 比较操作符 == and != 支持布尔属性;
- 对于 NSString 和 NSData 属性来说,我们支持 ==、!=、BEGINSWITH、CONTAINS 以及 ENDSWITH 操作符,比如说 name CONTAINS ‘Ja’;
- 字符串支持忽略大小写的比较方式,比如说 name CONTAINS[c] ‘Ja’ ,注意到其中字符的大小写将被忽略;
- Realm 支持以下复合操作符:“AND”、“OR” 以及 “NOT”。比如说 name BEGINSWITH ‘J’ AND age >= 32;
- 包含操作符 IN,比如说 name IN {‘Lisa’, ‘Spike’, ‘Hachi’};
- ==、!=支持与 nil 比较,比如说 Results<Company>().filter("ceo == nil")。注意到这只适用于有关系的对象,这里 ceo 是 Company 模型的一个属性。
- ANY 比较,比如说 ANY student.age < 21
- 注意,虽然我们不支持复合表达式类型(aggregate expression type),但是我们支持对对象的值使用 BETWEEN 操作符类型。比如说,Results<Person>.filter("age BETWEEN %@", [42, 43]])。
1
2
|
//查询花费超过10元的消费记录,并按升序排列
consumeItems
= realm.objects( ConsumeItem . self ). filter ( "cost
> 10" ).sorted(byKeyPath: "cost" )
|
12,使用List实现一对多关系
List 中可以包含简单类型的 Object,表面上和可变的 Array 非常类似。
注意:List 只能够包含 Object 类型,不能包含诸如String之类的基础类型。
如果打算给我们的 Person 数据模型添加一个“dogs”属性,以便能够和多个“dogs”建立关系,也就是表明一个 Person 可以有多个 Dog,那么我们可以声明一个List类型的属性:
1
2
3
4
5
6
7
8
9
10
|
class Person : Object {
... //
其余的属性声明
let dogs
= List < Dog >()
}
//
这里我们就可以使用已存在的狗狗对象来完成初始化
let aPerson
= Person (value:
[ "李四" ,
30, [aDog, anotherDog]])
//
还可以使用多重嵌套
let aPerson
= Person (value:
[ "李四" ,
30, [[ "小黑" ,
5], [ "旺财" ,
6]]])
|
1
2
3
|
let someDogs
= realm.objects( Dog . self ). filter ( "name
contains '小白'" )
ZhangSan .dogs.append(objectsIn:
someDogs)
ZhangSan .dogs.append(dahuang)
|
通过反向关系(也被称为反向链接(backlink)),您可以通过一个特定的属性获取和给定对象有关系的所有对象。 Realm 提供了“链接对象 (linking objects)”
属性来表示这些反向关系。借助链接对象属性,您可以通过指定的属性来获取所有链接到指定对象的对象。
例如,一个 Dog 对象可以拥有一个名为 owners 的链接对象属性,这个属性中包含了某些 Person 对象,而这些 Person 对象在其 dogs 属性中包含了这一个确定的 Dog 对象。您可以将 owners 属性设置为 LinkingObjects 类型,然后指定其关系,说明其当中包含了
Person 对象。
1
2
3
4
5
6
7
8
|
class Dog : Object {
@objc dynamic var name
= ""
@objc dynamic var age
= 0
//
Realm 并不会存储这个属性,因为这个属性只定义了 getter
//
定义“owners”,和 Person.dogs 建立反向关系
let owners
= LinkingObjects (fromType: Person . self ,
property: "dogs" )
}
|
13,添加主键(Primary Keys)
重写 Object.primaryKey() 可以设置模型的主键。
声明主键之后,对象将被允许查询,更新速度更加高效,并且要求每个对象保持唯一性。
一旦带有主键的对象被添加到 Realm 之后,该对象的主键将不可修改。
1
2
3
4
5
6
7
8
|
class Person : Object {
@objc dynamic var id
= 0
@objc dynamic var name
= ""
override static func primaryKey()
-> String ?
{
return "id"
}
}
|
14,添加索引属性(Indexed Properties)
重写 Object.indexedProperties() 方法可以为数据模型中需要添加索引的属性建立索引:
1
2
3
4
5
6
7
8
|
class Book : Object {
@objc dynamic var price
= 0
@objc dynamic var title
= ""
override static func indexedProperties()
-> [ String ]
{
return [ "title" ]
}
}
|
15,设置忽略属性(Ignored Properties)
重写 Object.ignoredProperties() 可以防止 Realm 存储数据模型的某个属性。Realm 将不会干涉这些属性的常规操作,它们将由成员变量(var)提供支持,并且您能够轻易重写它们的
setter 和 getter。
1
2
3
4
5
6
7
8
9
10
11
12
|
class Person : Object {
@objc dynamic var tmpID
= 0
var name: String { //
计算属性将被自动忽略
return "\(firstName)
\(lastName)"
}
@objc dynamic var firstName
= ""
@objc dynamic var lastName
= ""
override static func ignoredProperties()
-> [ String ]
{
return [ "tmpID" ]
}
}
|
16,修改更新数据
(1)直接更新内容
(2)通过主键更新
如果您的数据模型中设置了主键的话,那么您可以使用 Realm().add(_:update:) 来更新对象(当对象不存在时也会自动插入新的对象。)
1
2
3
4
|
//
在一个事务中更新对象
try!
realm.write {
consumeItem.name
= "去北京旅行"
}
|
如果您的数据模型中设置了主键的话,那么您可以使用 Realm().add(_:update:) 来更新对象(当对象不存在时也会自动插入新的对象。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/******
方式1 ***/
//
创建一个带有主键的“书籍”对象,作为事先存储的书籍
let cheeseBook
= Book ()
cheeseBook.title
= "奶酪食谱"
cheeseBook.price
= 9000
cheeseBook.id
= 1
//
通过 id = 1 更新该书籍
try!
realm.write {
realm.add(cheeseBook,
update: true )
}
/******
方式2 ***/
//
假设带有主键值 `1` 的“书籍”对象已经存在
try!
realm.write {
realm.create( Book . self ,
value: [ "id" :
1, "price" :
22], update: true )
//
这本书的`title`属性不会被改变
}
|
这个是在运行时才能决定哪个属性需要更新的时候,这个对于大量更新的对象极为有用。
1
2
3
4
5
6
7
|
let persons
= realm.objects( Person . self )
try!
realm.write {
//
更新第一个
persons.first?.setValue( true ,
forKeyPath: "isFirst" )
//
将每个人的 planet 属性设置为“地球”
persons.setValue( "地球" ,
forKeyPath: "planet" )
}
|
17,删除数据
1
2
3
4
5
6
|
let cheeseBook
= ... //
存储在 Realm 中的 Book 对象
//
在事务中删除一个对象
try!
realm.write {
realm.delete(cheeseBook)
}
|
1
2
3
4
|
//
从 Realm 中删除所有数据
try!
realm.write {
realm.deleteAll()
}
|
18,Realm数据库配置
(1)修改默认的的数据库
通过调用 Realm() 来初始化以及访问我们的 realm 变量。其指向的是应用的 Documents 文件夹下的一个名为“default.realm”的文件。
通过对默认配置进行更改,我们可以使用不同的数据库。比如给每个用户帐号创建一个特有的 Realm 文件,通过切换配置,就可以直接使用默认的 Realm 数据库来直接访问各自数据库:
1
2
3
4
5
6
7
8
9
10
|
func setDefaultRealmForUser(username: String )
{
var config
= Realm . Configuration ()
//
使用默认的目录,但是使用用户名来替换默认的文件名
config.fileURL
= config.fileURL!.deletingLastPathComponent()
.appendingPathComponent( "\(username).realm" )
//
将这个配置应用到默认的 Realm 数据库当中
Realm . Configuration .defaultConfiguration
= config
}
|
如果需要将应用的某些数据(比如配置信息,初始化信息等)打包到一个 Realm 文件中,作为主要 Realm 数据库的扩展,操作如下:
1
2
3
4
5
6
7
8
9
10
11
|
let config
= Realm . Configuration (
//
获取需要打包文件的 URL 路径
fileURL: Bundle .main.url(forResource: "MyBundledData" ,
withExtension: "realm" ),
//
以只读模式打开文件,因为应用数据包并不可写
readOnly: true )
//
通过配置打开 Realm 数据库
let realm
= try! Realm (configuration:
config)
//
通过配置打开 Realm 数据库
let results
= realm.objects( Dog . self ). filter ( "age
> 5" )
|
内存数据库在每次程序运行期间都不会保存数据。但是,这不会妨碍到 Realm 的其他功能,包括查询、关系以及线程安全。 假如您需要灵活的数据读写但又不想储存数据的话,那么内存数据库对您来说一定是一个不错的选择。
1
|
let realm
= try! Realm (configuration: Realm . Configuration (inMemoryIdentifier: "MyInMemoryRealm" ))
|
19,加密数据库
(1)加密后的 Realm文件不能跨平台使用(因为 NSFileProtection 只有 iOS 才可以使用)
(2)Realm 文件不能在没有密码保护的 iOS 设备中进行加密。为了避免这些问题(或者您想构建一个 OS
X 的应用),可以使用 Realm 提供的加密方法。
(3)加密过的 Realm 只会带来很少的额外资源占用(通常最多只会比平常慢10%)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/*****
在创建 Realm 数据库时采用64位的**对数据库文件进行 AES-256+SHA2 加密 ****/
//
产生随机**
var key
= Data (count:
64)
_
= key.withUnsafeMutableBytes { bytes in
SecRandomCopyBytes (kSecRandomDefault,
64, bytes)
}
//
打开加密文件
let config
= Realm . Configuration (encryptionKey:
key)
let realm: Realm
do
{
realm
= try Realm (configuration:
config)
}
catch let error as NSError {
//
如果**错误,`error` 会提示数据库不可访问
fatalError( "Error
opening realm: \(error)" )
}
//
和往常一样使用 Realm 即可
let dogs
= realm.objects( Book . self ). filter ( "name
contains 'Fido'" )
|
20,数据迁移(Migration)
(1)为何要迁移
比如原来有如下 Person 模型:
1
2
3
4
5
|
class Person : Object {
@objc dynamic var firstName
= ""
@objc dynamic var lastName
= ""
@objc dynamic var age
= 0
}
|
1
2
3
4
|
class Person : Object {
@objc dynamic var fullName
= ""
@objc dynamic var age
= 0
}
|
(2)如何进行数据迁移
假设我们想要把上面所声明 Person 数据模型进行迁移。如下所示是最简单的数据迁移的必需流程:
虽然这个迁移操作是最精简的了,但是我们需要让这个闭包能够自行计算新的属性(这里指的是 fullName),这样才有意义。 在迁移闭包中,我们能够调用Migration().enumerateObjects(_:_:) 来枚举特定类型的每个
Object 对象,然后执行必要的迁移逻辑。注意,对枚举中每个已存在的 Object 实例来说,应该是通过访问 oldObject 对象进行访问,而更新之后的实例应该通过 newObject 进行访问:
假设我们想要把上面所声明 Person 数据模型进行迁移。如下所示是最简单的数据迁移的必需流程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
//
在(application:didFinishLaunchingWithOptions:)中进行配置
let config
= Realm . Configuration (
//
设置新的架构版本。这个版本号必须高于之前所用的版本号
//
(如果您之前从未设置过架构版本,那么这个版本号设置为 0)
schemaVersion:
1,
//
设置闭包,这个闭包将会在打开低于上面所设置版本号的 Realm 数据库的时候被自动调用
migrationBlock:
{ migration, oldSchemaVersion in
//
目前我们还未进行数据迁移,因此 oldSchemaVersion == 0
if (oldSchemaVersion
< 1) {
//
什么都不要做!Realm 会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构
}
})
//
告诉 Realm 为默认的 Realm 数据库使用这个新的配置对象
Realm . Configuration .defaultConfiguration
= config
//
现在我们已经告诉了 Realm 如何处理架构的变化,打开文件之后将会自动执行迁移
let realm
= try! Realm ()
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//
在 application(application:didFinishLaunchingWithOptions:) 中进行配置
Realm . Configuration .defaultConfiguration
= Realm . Configuration (
schemaVersion:
1,
migrationBlock:
{ migration, oldSchemaVersion in
if (oldSchemaVersion
< 1) {
//
enumerateObjects(ofType:_:) 方法遍历了存储在 Realm 文件中的每一个“Person”对象
migration.enumerateObjects(ofType: Person .className())
{ oldObject, newObject in
//
将名字进行合并,存放在 fullName 域中
let firstName
= oldObject![ "firstName" ] as ! String
let lastName
= oldObject![ "lastName" ] as ! String
newObject![ "fullName" ]
= "\(firstName)
\(lastName)"
}
}
})
|
21,使用带有 REST API 功能的 Realm 数据库示例
我们将从 豆瓣FM的API 那里获取一组 JSON 格式的频道数据,然后将它以 Realm Objects 的形式储存到默认的 Realm 数据库里。
(1)json数据格式如下:
(2)我们将直接把 Dictionary 插入到 Realm 中,然后让 Realm 自行快速地将其映射到 Object 上。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
{
"channels":
[
{
"name_en":
"Personal Radio",
"seq_id":
0,
"abbr_en":
"My",
"name":
"私人兆赫",
"channel_id":
0
},
{
"name":
"华语",
"seq_id":
0,
"abbr_en":
"",
"channel_id":
"1",
"name_en":
""
},
{
"name":
"欧美",
"seq_id":
1,
"abbr_en":
"",
"channel_id":
"2",
"name_en":
""
}
]
}
|
(从 iOS9 起,新特性要求App访问网络请求,要采用 HTTPS 协议。直接请求HTTP数据会报错,解决办法可以参照的我另一篇文章:Swift
- 网络请求报App Transport Security has blocked a cleartext错)
为了确保示例能够成功,我们需要一个所有属性完全匹配 JSON 键结构的 Object 结构体。如果 JSON 的键结构不匹配 Object 结构体属性结构的话,那么就会在插入时被忽略。
(3)可以看到数据已经成功插入到库中了
22,当前版本的限制
为了确保示例能够成功,我们需要一个所有属性完全匹配 JSON 键结构的 Object 结构体。如果 JSON 的键结构不匹配 Object 结构体属性结构的话,那么就会在插入时被忽略。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
import UIKit
import RealmSwift
class ViewController : UIViewController {
override func viewDidLoad()
{
super .viewDidLoad()
//
调用API
let response
= try! Data (contentsOf:
url)
//
对 JSON 的回应数据进行反序列化操作
let json
= try! JSONSerialization .jsonObject(with:
response,
options:
.allowFragments) as !
[ String : Any ]
let channels
= json[ "channels" ] as !
[[ String : Any ]]
let realm
= try! Realm ()
try!
realm.write {
//
为数组中的每个元素保存一个对象(以及其依赖对象)
for channel in channels
{
if channel[ "seq_id" ] as ! Int ==
0 { continue } //第一个频道数据有问题,丢弃掉
realm.create( Dou*nel . self ,
value: channel, update: true )
}
}
print (realm.configuration.fileURL
?? "" )
}
override func didReceiveMemoryWarning()
{
super .didReceiveMemoryWarning()
}
}
//豆瓣频道
class Dou*nel : Object {
//频道id
@objc dynamic var channel_id
= ""
//频道名称
@objc dynamic var name
= ""
//频道英文名称
@objc dynamic var name_en
= ""
//排序
@objc dynamic var seq_id
= 0
@objc dynamic var abbr_en
= ""
//设置主键
override static func primaryKey()
-> String ?
{
return "channel_id"
}
}
|
(3)可以看到数据已经成功插入到库中了
22,当前版本的限制
Realm 致力于平衡数据库读取的灵活性和性能。为了实现这个目标,在 Realm 中所存储的信息的各个方面都有基本的限制。例如:
(1)类名称的长度最大只能存储 57 个 UTF8 字符。
(2)属性名称的长度最大只能支持 63 个 UTF8 字符。
(3)NSData 以及 String 属性不能保存超过 16
MB 大小的数据。如果要存储大量的数据,可通过将其分解为16MB 大小的块,或者直接存储在文件系统中,然后将文件路径存储在 Realm 中。如果您的应用试图存储一个大于 16MB 的单一属性,系统将在运行时抛出异常。
(4)对字符串进行排序以及不区分大小写查询只支持“基础拉丁字符集”、“拉丁字符补充集”、“拉丁文扩展字符集 A” 以及”拉丁文扩展字符集 B“(UTF-8 的范围在 0~591 之间)。
原文出自:www.hangge.com 原文链接:http://www.hangge.com/blog/cache/detail_891.html