realm-cocoa 學(xué)習(xí)

PS:以下內(nèi)容均為項目為Swift的基礎(chǔ),如果需要Object-c請查閱Realm的Object-c文檔。

前些日子在看第一屆《中國Swift開發(fā)者大會》的時候,聽到了realm這個數(shù)據(jù)庫,說到了運行速度方面,相較于CoreData可以有很大的提升。再加上之前在做項目的時候,覺的coredata的很多配置讓我覺的不爽,很是繁瑣。所以就想看一看realm到底如何,學(xué)習(xí)了一天覺的很好,固決定記錄一下。因為也不知道怎么講解,所以我會嘗試翻譯realm官網(wǎng)的文檔,再加上我的理解進行解釋。PS:寫完博客之后發(fā)覺,人家有中文API…。

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

這是Realm官網(wǎng)的對于自己的解釋。

realm可以快速有效的編寫應(yīng)用程序的本地持久化,它的代碼大概就是下面這個樣子:

// 先創(chuàng)建一個你想要持久化的對象的模型,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對象的時候,就像咱們平時創(chuàng)建Class對象一樣的
let myDog = Dog()
myDog.name = "Rex"
myDog.age = 1
print("name of dog: \\(myDog.name)")

// 得到一個Realm對象
let realm = try! Realm()

// 查詢這個狗小于兩歲的,其實咱們看著代碼就可以理解了,它使用了鏈?zhǔn)酱a結(jié)構(gòu),很容易看懂
let puppies = realm.objects(Dog).filter("age < 2")
puppies.count // => 0 比如現(xiàn)在的到的數(shù)目是0,因為現(xiàn)在咱們還沒有添加任何數(shù)據(jù)

// 把咱們的上面創(chuàng)建的狗的對象,添加到數(shù)據(jù)庫中
try! realm.write {
  realm.add(myDog)
}

// 再次查看數(shù)量的時候,不需要再進行一次查詢,Realm會自動的為你完成更新
puppies.count // => 1

// 你也可以更新你的數(shù)據(jù)內(nèi)容在任何線程
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安裝包最新版本
  • 根據(jù)你的需要,將你下載文件解壓后, 從/ios/swift-2.1.1(IOS設(shè)備) 或者 tvos/(Apple電視設(shè)備) 再或者 osx/swift-2.1.1/(mac電腦設(shè)備) 這三個目錄中,選擇自己需要的文件,將其拖入項目中,并且要選中“如果需要請勾選”選項
  • 在你的項目中 Build Settings ,為Framework Search Paths 設(shè)置ealmSwift.framework 路徑
  • 如果使用Realm 在 IOS,watchOS或者TvOS,在Build Phase 創(chuàng)建一個新的 “Run Script Phase”,下面寫上bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework/strip-frameworks.sh"這一步是需要解決應(yīng)用程序商店提交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”設(shè)置項中,點擊“+”好按鈕,添加一個新的“New Run Script Phase”,內(nèi)容如下/usr/local/bin/carthage copy-frameworks和路徑添加到框架下您想要使用“輸入文件”,如$(SRCROOT)/Carthage/Build/iOS/Realm.framework,
    $(SRCROOT)/Carthage/Build/iOS/RealmSwift.framework這個腳本是在應(yīng)用程序商店提交錯誤引發(fā)了普遍的二進制文件。確保這一階段后,“嵌入框架”階段。

<a name='方便快捷的創(chuàng)建Realm模型'></a>

方便快捷的創(chuàng)建Realm模型

首先需要安裝Alcatraz,在終端里面執(zhí)行以下命令.如果執(zhí)行失敗,請翻墻后在嘗試

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

安裝完成之后,重啟xcode就可以在菜單window選項下面看到一個Package Manager 選項,點擊 安裝package ,搜索 Xcode Plugin ,之后,再重啟。創(chuàng)建創(chuàng)建文件,就可以文件類型中有一個Realm選項,點擊創(chuàng)建。之后生成文件! 你會發(fā)現(xiàn)??!SHIT?。。。?!我不如自己寫了?。。。。。?!

<a name='創(chuàng)建Realm 數(shù)據(jù)模型'></a>

創(chuàng)建Realm 數(shù)據(jù)模型

其實創(chuàng)建Realm數(shù)據(jù)模型的方法超級簡單,就和咱們平時創(chuàng)建Class是一樣一樣的。

import RealmSwift

// 狗的數(shù)據(jù)模型
class Dog: Object {
    dynamic var name = ""
    dynamic var owner: Person? // 狗的主人是可以為空的
}

但是,在們必須知道他都允許咱們創(chuàng)建什么類型的數(shù)據(jù)。

這樣子咱們創(chuàng)建數(shù)據(jù)模型的時候就沒有問題了吧。當(dāng)然Realm是支持綁定關(guān)系的。一對一,一對多之類的,都可以的。

<a name='數(shù)據(jù)的操作'></a>

數(shù)據(jù)的操作

<a name='向Realm插入數(shù)據(jù)'></a>

向Realm插入數(shù)據(jù)

終于迎來了,咱們的增刪改查了。

當(dāng)你定義一個模型你可以實例化對象子類和新實例添加到域??紤]一下這個簡單的模型:

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

我們可以以下幾個方式創(chuàng)建對象:

// (1) 先創(chuàng)建狗的對象,然后在給予其賦值
var myDog = Dog()
myDog.name = "Rex"
myDog.age = 10

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

// (3) 使用一個Array創(chuàng)建y。
let myThirdDog = Dog(value: ["Fido", 5])

1.最顯而易見的就是 直接創(chuàng)建對象,并且賦值
2.也可以使用字典實例化對象,但是!要使用數(shù)據(jù)模型中對應(yīng)的!的鍵和值。
3.最后,可以使用數(shù)組實例化對象。但是要注意!數(shù)組中的值,必須和數(shù)據(jù)模型中的屬性的順序保持一致

創(chuàng)建完成對象之后就是把它放入數(shù)據(jù)庫里面了,你可以像下面的方法一樣放進數(shù)據(jù)庫

// 創(chuàng)建一個用戶對象,并且賦值
let author = Person()
author.name = "David Foster Wallace"

// 得到默認的Realm對象
let realm = try! Realm()
// 你只需要這么做一次

// 添加這個對象到Realm
try! realm.write {
  realm.add(author)
}

這樣就在數(shù)據(jù)庫中,添加了一個對象了。

<a name='Realm修改數(shù)據(jù)'></a>

Realm修改數(shù)據(jù)

Realm 提供很多方式去修改數(shù)據(jù),請選擇適合自己的方式進行修改

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

Typed Updates

你可以修改任何對象的屬性,在write進程中

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

<a name='根據(jù)數(shù)據(jù)的主鍵進行修改'></a>

根據(jù)數(shù)據(jù)的主鍵進行修改

重寫數(shù)據(jù)模型的Object.primaryKey()方法可以設(shè)置模型的主鍵,聲明一個主鍵,允許對象可以通過主鍵進行有效的查詢和修改,并強制每個值的唯一性。一旦一個對象添加到Realm,主鍵就不能更改

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

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

因為這個數(shù)據(jù)模型含有主鍵,所以再向Realm添加一個對象時,如果已經(jīng)存在同樣主鍵的對象時,Realm會自動更新前一個數(shù)據(jù)的其它不一樣的屬性。而如果不存在這個主鍵的對象,Realm會創(chuàng)建一個新的對象

// 假設(shè)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)
}

你也可以通過一個字典的方式,指定主鍵和你要修改的屬性,來進行修改用戶

// 假設(shè)數(shù)據(jù)庫中已經(jīng)存在一個主鍵為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='修改大批量數(shù)據(jù)'></a>

修改大批量數(shù)據(jù)

可以獲取一群數(shù)據(jù)后,指定這些數(shù)據(jù)其中的某些數(shù)據(jù),或者全部數(shù)據(jù)。 的某一歇屬性值。

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

<a name='Realm刪除數(shù)據(jù)'></a>

Realm刪除數(shù)據(jù)

當(dāng)用戶想刪除某一個對象的時候,查詢出來,之后~

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

//刪除一個對象,在Wirte交易中。
try! realm.write {
  realm.delete(cheeseBook)
}

你也可以刪除Realm中的全部數(shù)據(jù),這很快速

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

<a name='Realm查詢數(shù)據(jù)'></a>

Realm查詢數(shù)據(jù)

Realm稱為這么受歡迎的數(shù)據(jù)庫的原因來了!咱們看她們官方的解釋。

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.

查詢返回一個結(jié)果實例,它包含一個對象集合。結(jié)果類似于Array一樣的東西,可以使用下標(biāo)來進行每一個數(shù)據(jù)的訪問。和Arrays不一樣的地方在于,查詢結(jié)果只持有一種類型的對象。

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

在Realm中,所有的查詢(這里面包括了查詢和屬性的訪問)都是懶加載的。數(shù)據(jù)僅僅在數(shù)據(jù)訪問的時候再進行唯一的一次讀取

那么就讓我們來試試吧。

let dogs = realm.objects(Dog) // 這樣子就查詢了Realm數(shù)據(jù)庫中所有的狗

<a name='條件篩選'></a>

條件篩選

如果你熟悉nspredicate,那么你已經(jīng)到了指導(dǎo)如何查詢的境界。對象、Realms,列表,和結(jié)果都提供了方法,查詢一個結(jié)果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列表。領(lǐng)域支持許多共同的謂詞:

  • 比較操作數(shù)可以是屬性名稱或常量。至少一個操作數(shù)必須是一個屬性名。
  • 比較運算符= =,<,> =,>,!=,支持Int, Int8, Int16, Int32, Int64, Float, DoubleNSDate 屬性類型。如: age == 45
  • 身份比較 == ,!= ,Results<Employee>().filter("company == %@", company)
  • 比較運算符 = 和 != 需要Bool屬性的支持。
  • 字符串和NSData性質(zhì),我們我們支持 ==, !=,BEGINSWITH,CONTAINS, andENDSWITH ,如名稱中 CONTAINS ‘Ja’
  • 不區(qū)分大小寫的比較字符串,如:CONTAINS[c] ‘Ja’。請注意只有字符A-Za-z會被忽略。
  • Realm支持以下復(fù)合操作:: 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必須和一個常量進行比較
  • 相關(guān)子查詢現(xiàn)在還不支持

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

排序

結(jié)果允許您在一個或多個屬性的基礎(chǔ)上指定一個排序標(biāo)準(zhǔn)和順序。例如,下面的例子中狗按照名字來進行排序返回:

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

<a name='鏈?zhǔn)讲樵?></a>

鏈?zhǔn)讲樵?/strong>

Realm 和其它的查區(qū)別在于,他每一個查詢結(jié)果都擁有繼續(xù)查詢的能力,這樣用戶就可以對自己查詢的結(jié)果進行進一步的詢問。例如:我們先查找了一群棕色的狗,之后又想查詢處其中名字以“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(
  // 設(shè)置一個新的版本,如果你從來沒有設(shè)置過版本,那么默認版本就是0
  schemaVersion: 1,

  /// 設(shè)置一個找到不一樣版本的回調(diào)方法
  migrationBlock: { migration, oldSchemaVersion in
    // 因為我們沒有設(shè)置過版本,所以 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
        // 設(shè)置到新的對象中的屬性上去
        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數(shù)據(jù)變化通知'></a>

Realm數(shù)據(jù)變化通知

結(jié)果或列表是通過調(diào)用addnotificationblock方法在其數(shù)值發(fā)生變化的時候給予通知這是可能的。
每一次寫事務(wù)提交時,在其他線程上的其他線程發(fā)送通知到其他實例:

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

// 稍后
token.stop()

當(dāng)然你也可以注冊某一范圍的數(shù)值發(fā)生變化的時候,進行給予通知

// 大于5歲的人年紀(jì)發(fā)生變化的時候進行統(tǒng)治
let token = realm.objects(Person).filter("age > 5").addNotificationBlock { results, error in
    // results 就已經(jīng)是 `realm.objects(Person).filter("age > 5")`的結(jié)果集了
    viewController.updateUI()
}
// 稍后
token.stop()

其它更詳細的文檔請訪問 Realm-Swift文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容