realm-cocoa 学习

PS:以下内容均为项目为Swift的基础,如果需要Object-c请查阅Realm的Object-c文档

前些日子在看第一届《中国Swift开发者大会》的时候,听到了realm这个数据库,说到了运行速度方面,相较于CoreData可以有很大的提升。再加上之前在做项目的时候,觉的coredata的很多配置让我觉的不爽,很是繁琐。所以就想看一看realm到底如何,学习了一天觉的很好,固决定记录一下。因为也不知道怎么讲解,所以我会尝试翻译realm官网的文档,再加上我的理解进行解释。PS:写完博客之后发觉,人家有中文API…。

Realm is a mobile database
hundreds of millions of people rely on

这是Realm官网的对于自己的解释。

realm可以快速有效的编写应用程序的本地持久化,它的代码大概就是下面这个样子:

// 先创建一个你想要持久化的对象的模型,Object 是 Reaml 自己定义的对象
class Dog: Object {
  dynamic var name = ""
  dynamic var age = 0
}
class Person: Object {
  dynamic var name = ""
  dynamic var picture: NSData? = nil // 这是一个可选类型
  let dogs = List<Dog>()
}

// 这是实例化一个Realm对象的时候,就像咱们平时创建Class对象一样的
let myDog = Dog()
myDog.name = "Rex"
myDog.age = 1
print("name of dog: \\(myDog.name)")

// 得到一个Realm对象
let realm = try! Realm()

// 查询这个狗小于两岁的,其实咱们看着代码就可以理解了,它使用了链式代码结构,很容易看懂
let puppies = realm.objects(Dog).filter("age < 2")
puppies.count // => 0 比如现在的到的数目是0,因为现在咱们还没有添加任何数据

// 把咱们的上面创建的狗的对象,添加到数据库中
try! realm.write {
  realm.add(myDog)
}

// 再次查看数量的时候,不需要再进行一次查询,Realm会自动的为你完成更新
puppies.count // => 1

// 你也可以更新你的数据内容在任何线程
dispatch_async(dispatch_queue_create("background", nil)) {
  let realm = try! Realm()
  let theDog = realm.objects(Dog).filter("age == 1").first
  try! realm.write {
    theDog!.age = 3
  }
}

是不是很方便。那么接下来咱们就从几个简单的方面,来完成本次Realm的教程吧。

项目中如何添加 Realm 框架

  1. 使用Dynamic Framework:
  • 下载Realm安装包最新版本
  • 根据你的需要,将你下载文件解压后, 从/ios/swift-2.1.1(IOS设备) 或者 tvos/(Apple电视设备) 再或者 osx/swift-2.1.1/(mac电脑设备) 这三个目录中,选择自己需要的文件,将其拖入项目中,并且要选中“如果需要请勾选”选项
  • 在你的项目中 Build Settings ,为Framework Search Paths 设置ealmSwift.framework 路径
  • 如果使用Realm 在 IOS,watchOS或者TvOS,在Build Phase 创建一个新的 “Run Script Phase”,下面写上bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework/strip-frameworks.sh"这一步是需要解决应用程序商店提交bug时存档通用二进制文件。
  1. 使用CocoaPods
  • 安装CocoaPods0.39.0 版本或者更新的版本.
  • 在你的Podfile文件中添加use_frameworks!pod 'RealmSwift'
  • 在终端中运行 pod install
  • 打开 *.xcworkspace 项目
  1. 使用Carthage
  • 安装 Carthage 0.9.2 或者更新的版本
  • 在你的Cartfile文件中添加"realm/realm-cocoa"
  • 运行 carthage update.
  • 从 Carthage/Build/ 拖拽RealmSwift.framework 和 Realm.framework 到 General 下面的“Embedded Binaries”
  • iOS/watchOS/tvOS: 在你的项目targets的 “Build Phases”设置项中,点击“+”好按钮,添加一个新的“New Run Script Phase”,内容如下/usr/local/bin/carthage copy-frameworks和路径添加到框架下您想要使用“输入文件”,如$(SRCROOT)/Carthage/Build/iOS/Realm.framework,
    $(SRCROOT)/Carthage/Build/iOS/RealmSwift.framework这个脚本是在应用程序商店提交错误引发了普遍的二进制文件。确保这一阶段后,“嵌入框架”阶段。

<a name='方便快捷的创建Realm模型'></a>

方便快捷的创建Realm模型

首先需要安装Alcatraz,在终端里面执行以下命令.如果执行失败,请翻墙后在尝试

curl -fsSL https://raw.githubusercontent.com/supermarin/Alcatraz/deploy/Scripts/install.sh | sh

安装完成之后,重启xcode就可以在菜单window选项下面看到一个Package Manager 选项,点击 安装package ,搜索 Xcode Plugin ,之后,再重启。创建创建文件,就可以文件类型中有一个Realm选项,点击创建。之后生成文件! 你会发现!!SHIT!!!!!我不如自己写了!!!!!!!

<a name='创建Realm 数据模型'></a>

创建Realm 数据模型

其实创建Realm数据模型的方法超级简单,就和咱们平时创建Class是一样一样的。

import RealmSwift

// 狗的数据模型
class Dog: Object {
    dynamic var name = ""
    dynamic var owner: Person? // 狗的主人是可以为空的
}

但是,在们必须知道他都允许咱们创建什么类型的数据。

这样子咱们创建数据模型的时候就没有问题了吧。当然Realm是支持绑定关系的。一对一,一对多之类的,都可以的。

<a name='数据的操作'></a>

数据的操作

<a name='向Realm插入数据'></a>

向Realm插入数据

终于迎来了,咱们的增删改查了。

当你定义一个模型你可以实例化对象子类和新实例添加到域。考虑一下这个简单的模型:

class Dog: Object {
    dynamic var name = ""
    dynamic var age = 0
}

我们可以以下几个方式创建对象:

// (1) 先创建狗的对象,然后在给予其赋值
var myDog = Dog()
myDog.name = "Rex"
myDog.age = 10

// (2) 使用字典创建
let myOtherDog = Dog(value: ["name" : "Pluto", "age": 3])

// (3) 使用一个Array创建y。
let myThirdDog = Dog(value: ["Fido", 5])

1.最显而易见的就是 直接创建对象,并且赋值
2.也可以使用字典实例化对象,但是!要使用数据模型中对应的!的键和值。
3.最后,可以使用数组实例化对象。但是要注意!数组中的值,必须和数据模型中的属性的顺序保持一致

创建完成对象之后就是把它放入数据库里面了,你可以像下面的方法一样放进数据库

// 创建一个用户对象,并且赋值
let author = Person()
author.name = "David Foster Wallace"

// 得到默认的Realm对象
let realm = try! Realm()
// 你只需要这么做一次

// 添加这个对象到Realm
try! realm.write {
  realm.add(author)
}

这样就在数据库中,添加了一个对象了。

<a name='Realm修改数据'></a>

Realm修改数据

Realm 提供很多方式去修改数据,请选择适合自己的方式进行修改

<a name='Typed Updates'></a>

Typed Updates

你可以修改任何对象的属性,在write进程中

// Update an object with a transaction
try! realm.write {
  author.name = "Thomas Pynchon"
}

<a name='根据数据的主键进行修改'></a>

根据数据的主键进行修改

重写数据模型的Object.primaryKey()方法可以设置模型的主键,声明一个主键,允许对象可以通过主键进行有效的查询和修改,并强制每个值的唯一性。一旦一个对象添加到Realm,主键就不能更改

class Book: Object {
  dynamic var id = 0
  dynamic var price = 0
  dynamic var title = ""

  override static func primaryKey() -> String? {
    return "id"
  }
}

因为这个数据模型含有主键,所以再向Realm添加一个对象时,如果已经存在同样主键的对象时,Realm会自动更新前一个数据的其它不一样的属性。而如果不存在这个主键的对象,Realm会创建一个新的对象

// 假设Realm中存在一个主键为1的对象的时候
let cheeseBook = Book()
cheeseBook.title = "Cheese recipes"
cheeseBook.price = 9000
cheeseBook.id = 1

// id==1,修改这个书的属性
try! realm.write {
  realm.add(cheeseBook, update: true)
}

你也可以通过一个字典的方式,指定主键和你要修改的属性,来进行修改用户

// 假设数据库中已经存在一个主键为1的Book时
try! realm.write {
  realm.create(Book.self, value: ["id": 1, "price": 9000.0], update: true)
  // the book's `title` property will remain unchanged.
}

<a name='修改大批量数据'></a>

修改大批量数据

可以获取一群数据后,指定这些数据其中的某些数据,或者全部数据。 的某一歇属性值。

let persons = realm.objects(Person)
try! realm.write {
 // 设置第一个人的 isFirst对象为True
 persons.first?.setValue(true, forKeyPath: "isFirst")
  // 把每个人的所居住的行星,设置为地球
  persons.setValue("Earth", forKeyPath: "planet")
}

<a name='Realm删除数据'></a>

Realm删除数据

当用户想删除某一个对象的时候,查询出来,之后~

// let cheeseBook = ... Book stored in Realm

//删除一个对象,在Wirte交易中。
try! realm.write {
  realm.delete(cheeseBook)
}

你也可以删除Realm中的全部数据,这很快速

// 删除Realm中所有的对象
try! realm.write {
  realm.deleteAll()
}

<a name='Realm查询数据'></a>

Realm查询数据

Realm称为这么受欢迎的数据库的原因来了!咱们看她们官方的解释。

Queries return a Results instance, which contains a collection of Objects. Results have an interface very similar to Array and objects contained in a Results can be accessed using indexed subscripting. Unlike Arrays, Results only hold Objects of a single subclass type.

查询返回一个结果实例,它包含一个对象集合。结果类似于Array一样的东西,可以使用下标来进行每一个数据的访问。和Arrays不一样的地方在于,查询结果只持有一种类型的对象。

All queries (including queries and property access) are lazy in Realm. Data is only read when the properties are accessed.

在Realm中,所有的查询(这里面包括了查询和属性的访问)都是懒加载的。数据仅仅在数据访问的时候再进行唯一的一次读取

那么就让我们来试试吧。

let dogs = realm.objects(Dog) // 这样子就查询了Realm数据库中所有的狗

<a name='条件筛选'></a>

条件筛选

如果你熟悉nspredicate,那么你已经到了指导如何查询的境界。对象、Realms,列表,和结果都提供了方法,查询一个结果Arrays 可以传递一个nspredicate实例,谓词字符串,或谓格式字符串。
例如,检索所有狗的颜色棕褐色和名字首“B”从默认的境界:

// 查询使用谓词字符串
var tanDogs = realm.objects(Dog).filter("color = 'tan' AND name BEGINSWITH 'B'")

// 查询使用 NSPredicate 实例
let predicate = NSPredicate(format: "color = %@ AND name BEGINSWITH %@", "tan", "B")
tanDogs = realm.objects(Dog).filter(predicate)

看到苹果的谓词编程指南建立谓词的更多信息和使用我们的nspredicate列表。领域支持许多共同的谓词:

  • 比较操作数可以是属性名称或常量。至少一个操作数必须是一个属性名。
  • 比较运算符= =,<,> =,>,!=,支持Int, Int8, Int16, Int32, Int64, Float, DoubleNSDate 属性类型。如: age == 45
  • 身份比较 == ,!= ,Results<Employee>().filter("company == %@", company)
  • 比较运算符 = 和 != 需要Bool属性的支持。
  • 字符串和NSData性质,我们我们支持 ==, !=,BEGINSWITH,CONTAINS, andENDSWITH ,如名称中 CONTAINS ‘Ja’
  • 不区分大小写的比较字符串,如:CONTAINS[c] ‘Ja’。请注意只有字符A-Za-z会被忽略。
  • Realm支持以下复合操作:: AND, OR, and NOT. 例如:name BEGINSWITH ‘J’ AND age >= 32
  • 包含操作符号 IN, 如 名字 name IN {‘Lisa’, ‘Spike’, ‘Hachi’}
  • 我们可以比较属性是否为空,如:Results<Company>().filter("ceo == nil").注意,境界把Nil作为一个特殊的值而不是一个属性的缺失,所以不像SQL零等于本身。
  • 任何的比较,如有 student.age<21
  • 聚合表达式@count, @min, @max, @sum@avgRealm都支持。例如:realm.objects(Company).filter("employees.@count > 5")。找到所有员工在五个以上的公司
  • 子查询的限制和支持:
  • @count在子查询表达式是唯一的。
  • SUBQUERY(…).@count必须和一个常量进行比较
  • 相关子查询现在还不支持

<a name='排序'></a>

排序

结果允许您在一个或多个属性的基础上指定一个排序标准和顺序。例如,下面的例子中狗按照名字来进行排序返回:

let sortedDogs = realm.objects(Dog).filter("color = 'tan' AND name BEGINSWITH 'B'").sorted("name")

<a name='链式查询'></a>

链式查询

Realm 和其它的查区别在于,他每一个查询结果都拥有继续查询的能力,这样用户就可以对自己查询的结果进行进一步的询问。例如:我们先查找了一群棕色的狗,之后又想查询处其中名字以“B”开头的狗。

let tanDogs = realm.objects(Dog).filter("color = 'tan'")
let tanDogsWithBNames = tanDogs.filter("name BEGINSWITH 'B'")

<a name='Realm版本迁移'></a>

Realm版本迁移

其实一般的版本迁移有两种情况,第一种。删除了某些字段

class Person: Object {
    dynamic var firstName = ""
    dynamic var lastName = ""
    dynamic var age = 0
}

To:

class Person: Object {
    dynamic var fullName = ""
    dynamic var age = 0
}

接下来,你将进行

// 在你的(application:didFinishLaunchingWithOptions:)

let config = Realm.Configuration(
  // 设置一个新的版本,如果你从来没有设置过版本,那么默认版本就是0
  schemaVersion: 1,

  /// 设置一个找到不一样版本的回调方法
  migrationBlock: { migration, oldSchemaVersion in
    // 因为我们没有设置过版本,所以 oldSchemaVersion < 1
    if (oldSchemaVersion < 1) {
      // 什么都不用做
      // Realm 将自动检测新的属性和删除的属性
      // 并会自动更新磁盘模式
    }
  })

// 告诉Realm 使用这个新的配置作为默认配置
Realm.Configuration.defaultConfiguration = config

// 接下来就让我们获得默认的对象吧
let realm = try! Realm()

而如果你添加了一些字段,比如咱们不想要FirstName和LastName了,我们希望他变成一个字段!fullName

// 在你的(application:didFinishLaunchingWithOptions:)

Realm.Configuration.defaultConfiguration = Realm.Configuration(
  schemaVersion: 1,
  migrationBlock: { migration, oldSchemaVersion in
    if (oldSchemaVersion < 1) {
      // 枚举所有的属性
      migration.enumerate(Person.className()) { oldObject, newObject in

        // 从旧的对象中获取之前存储的东西
        let firstName = oldObject!["firstName"] as! String
        let lastName = oldObject!["lastName"] as! String
        // 设置到新的对象中的属性上去
        newObject!["fullName"] = "\\(firstName) \\(lastName)"
      }
    }
  })

还有一种情况:从版本0 直接 升到 版本2

Realm.Configuration.defaultConfiguration = Realm.Configuration(
  schemaVersion: 2,
  migrationBlock: { migration, oldSchemaVersion in
    // The enumerateObjects:block: method iterates
    // over every 'Person' object stored in the Realm file
    migration.enumerate(Person.className()) { oldObject, newObject in
      // Add the `fullName` property only to Realms with a schema version of 0
      if oldSchemaVersion < 1 {
        let firstName = oldObject!["firstName"] as! String
        let lastName = oldObject!["lastName"] as! String
        newObject!["fullName"] = "\\(firstName) \\(lastName)"
      }

      // Add the `email` property to Realms with a schema version of 0 or 1
      if oldSchemaVersion < 2 {
          newObject!["email"] = ""
      }
    }
  })

// Realm will automatically perform the migration and opening the Realm will succeed
let realm = try! Realm()

<a name='Realm数据变化通知'></a>

Realm数据变化通知

结果或列表是通过调用addnotificationblock方法在其数值发生变化的时候给予通知这是可能的。
每一次写事务提交时,在其他线程上的其他线程发送通知到其他实例:

// 注册通知
let token = realm.addNotificationBlock { notification, realm in
    viewController.updateUI()
}

// 稍后
token.stop()

当然你也可以注册某一范围的数值发生变化的时候,进行给予通知

// 大于5岁的人年纪发生变化的时候进行统治
let token = realm.objects(Person).filter("age > 5").addNotificationBlock { results, error in
    // results 就已经是 `realm.objects(Person).filter("age > 5")`的结果集了
    viewController.updateUI()
}
// 稍后
token.stop()

其它更详细的文档请访问 Realm-Swift文档

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

推荐阅读更多精彩内容

  • Realm是由Y Combinator公司孵化出来的一款可以用于iOS(同样适用于Swift&Objective-...
    小歪子go阅读 2,206评论 6 9
  • 跨平台:现在很多应用都是要兼顾iOS和Android两个平台同时开发。如果两个平台都能使用相同的数据库,那就不用考...
    CoderZS阅读 2,462评论 2 16
  • 本文由我们团队的郭杰童鞋分享。 Realm是什么 Realm是由Y Combinator公司孵化出来的一款可以用于...
    知识小集阅读 2,589评论 4 16
  • Realm是什么 Realm是由Y Combinator公司孵化出来的一款可以用于iOS(同样适用于Swift&O...
    GJCode阅读 1,681评论 1 14
  • swift开发需要应用数据库,fmdb笔记繁琐,在swift中准备放弃使用,看到了 Realm这个三方框架很强大,...
    Kean_Qi阅读 6,966评论 9 12