歡迎加入QQ討論群258996829
一葉知秋 頭像
蘋果2袋
2
一葉知秋

Realm數(shù)據(jù)庫基礎(chǔ)教程

發(fā)布時間:2016-01-06 12:40  回復(fù):0  查看:3806   最后回復(fù):2016-01-06 12:40  

Realm數(shù)據(jù)庫基礎(chǔ)教程

學(xué)習(xí)如何使用 Realm 數(shù)據(jù)庫引擎來輕松地實現(xiàn) Swift 的數(shù)據(jù)存儲

Realm 是一個跨平臺的移動數(shù)據(jù)庫引擎,于 2014 年 7 月發(fā)布,準(zhǔn)確來說,它是專門為移動應(yīng)用所設(shè)計的數(shù)據(jù)持久化解決方案之一。

Realm 可以輕松地移植到您的項目當(dāng)中,并且絕大部分常用的功能(比如說插入、查詢等等)都可以用一行簡單的代碼輕松完成!

Realm 并不是對 Core Data 的簡單封裝,相反地, Realm 并不是基于 Core Data ,也不是基于 SQLite 所構(gòu)建的。它擁有自己的數(shù)據(jù)庫存儲引擎,可以高效且快速地完成數(shù)據(jù)庫的構(gòu)建操作。

之前我們提到過,由于 Realm 使用的是自己的引擎,因此, Realm 就可以在 iOS 和 Android 平臺上共同使用(完全無縫),并且支持 Swift 、 Objective-C 以及 Java 語言來編寫( Android 平臺和 iOS 平臺使用不同的 SDK )。

數(shù)以萬計的使用 Realm 的開發(fā)者都會發(fā)現(xiàn),使用 Realm 比使用 SQLite 以及 Core Data 要快很多。下面我們給出一個例子,分別展示 Core Data 和 Realm 在執(zhí)行一個斷言查詢請求并且排序結(jié)果所使用的代碼量:

// Core Data
let fetchRequest = NSFetchRequest(entityName: "Specimen")
let predicate = NSPredicate(format: "name BEGINSWITH [c]%@", searchString)
fetchRequest.predicate = predicate
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let error = NSError()
let results = managedObjectContext?.executeFetchRequest(fetchRequest, error:&error)
而換成了 Realm 呢?您會驚嘆于 Realm 的簡單的:
// Realm
let predicate = NSPredicate(format: "name BEGINSWITH [c]%@", searchString);
let specimens = Specimen.objectsWithPredicate(predicate).arraySortedByProperty("name", ascending: true)

使用 Realm 可以讓代碼變得十分簡潔,從而讓您的代碼易讀易寫。

綜上所述,我們之所以使用 Realm 的理由不外乎如下幾點:

  • 跨平臺 :現(xiàn)在絕大多數(shù)的應(yīng)用開發(fā)并不僅僅只在 iOS 平臺上進行開發(fā),還要兼顧到 Android 平臺的開發(fā)。為兩個平臺設(shè)計不同的數(shù)據(jù)庫是愚蠢的,而使用 Realm 數(shù)據(jù)庫, iOS 和 Android 無需考慮內(nèi)部數(shù)據(jù)的架構(gòu),調(diào)用 Realm 提供的 API 就可以完成數(shù)據(jù)的交換,實現(xiàn) “ 一個數(shù)據(jù)庫,兩個平臺無縫銜接 ” 。

  • 簡單易用 : Core Data 和 SQLite 冗余、繁雜的知識和代碼足以嚇退絕大多數(shù)剛?cè)腴T的開發(fā)者,而換用 Realm ,則可以極大地減少學(xué)習(xí)代價和學(xué)習(xí)時間,讓應(yīng)用及早用上數(shù)據(jù)存儲功能。

  • 可視化 : Realm 還提供了一個輕量級的數(shù)據(jù)庫查看工具,借助這個工具,開發(fā)者可以查看數(shù)據(jù)庫當(dāng)中的內(nèi)容,執(zhí)行簡單的插入和刪除數(shù)據(jù)的操作。畢竟,很多時候,開發(fā)者使用數(shù)據(jù)庫的理由是因為要提供一些所謂的 “ 知識庫 ” 。

本教程將會向您介紹 Realm 在 iOS 平臺上的簡單應(yīng)用,即導(dǎo)入 Realm 框架、創(chuàng)建數(shù)據(jù)模型、執(zhí)行查詢以及插入、更新和刪除記錄,以及使用既有的數(shù)據(jù)庫。

提示:原文教程寫于 2014 年,而 Realm 的版本更新得十分快,因此,本教程并不會拘泥于原文教程所述內(nèi)容,而是根據(jù) Realm 的版本更新進行相關(guān)修改。 
原文作者提到,要在 Realm 抵達 1.0 版本的時候再來更新這篇教程,大家盡請期待吧!

讓我們開始吧

我們將會以一個實際的項目來進行教程:假設(shè)您在西雙版納自然保護區(qū)覓得了一份職位 “ 監(jiān)測員 ” ,職責(zé)是記錄這個 “ 動植物王國 ” 當(dāng)中所發(fā)現(xiàn)物種的相關(guān)信息,包括種群數(shù)量、發(fā)現(xiàn)區(qū)域、年齡結(jié)構(gòu)等等。因此,您需要一個助手來幫忙記錄您的發(fā)現(xiàn),但是很可惜的是,保護區(qū)并沒有多余的人手來做您的助手(主要是沒錢)。所以沒有辦法,我們必須為自己制作一個虛擬的 “ 助手 ” ,也就是一個以 “ 物種監(jiān)測 ” 命名的 APP ,這樣就可以隨手記錄我們的發(fā)現(xiàn)了!

點擊此處下載本教程所使用的起始項目

在 Xcode 當(dāng)中打開我們的起始項目。此時, MapKit 已經(jīng)在項目當(dāng)中建立好了,而且項目已經(jīng)擁有了一些簡單的創(chuàng)建、更新和刪除物種信息的功能。

提示:如果您對 MapKit 的相關(guān)知識感興趣,可以查看 Introduction to MapKit tutorial ,這篇教程將會深入闡述 MapKit 是如何工作的。

現(xiàn)在,讓我們前往 Realm 的官網(wǎng)去下載 Realm 的框架吧: http://static.realm.io/downloads/cocoa/latest

Realm 的使用需求如下:

  • iOS ≥ 7 或者 Mac OS X ≥  10.9

  • Xcode ≥ 6

  • 現(xiàn)在 Realm 的版本為: 0.91.5

解壓下載下來的 Realm 壓縮包。在壓縮包中,我們可以看到一個名為 iOS 的文件夾。打開這個文件夾,然后將 Realm.framework 文件拖入到我們的起始項目中,最好拖放到 “Frameworks” 文件夾中以確保文件有序(強迫癥患者 ~ )。

Realm數(shù)據(jù)庫基礎(chǔ)教程

將框架文件拖入到項目當(dāng)中

之后,一定要確保勾選了 Copy Items if needed 選項,然后單擊 Finish 按鈕就完成了往項目中添加框架的操作。

之后,定位到項目設(shè)置中 SISpeciesNotes 的 General 選項卡,然后在 Link Binary with Libraries 欄目中添加 libc++.dylib 動態(tài)庫文件。

然后回到解壓的 Realm 文件夾中,打開名為 Swift 的文件夾,然后將里面的 RLMSupport.swift 文件拖入到項目當(dāng)中。這個文件包含了用于 Realm 相關(guān)類的 Swift 簡便方法,比如說 RLMResults 中的 Generator 方法,這樣就可以像使用原生數(shù)組一樣使用 Realm 數(shù)組了。

好的,我們的準(zhǔn)備工作就完成了!您可以嘗試運行一下起始項目,以確保沒有任何錯誤產(chǎn)生。如果出現(xiàn)錯誤的話,請仔細查看上面所述的一些步驟,確保沒有任何疏漏發(fā)生。運行成功后的基本界面如下所示:

Realm數(shù)據(jù)庫基礎(chǔ)教程

應(yīng)用界面

Realm Browser 介紹

Realm 資源包中包含了一個很有用的實用工具,可以幫助我們更好地管理 Realm 數(shù)據(jù)庫,那就是 Realm Browser 。

Realm Browser 可以讓您輕松地讀寫 Realm 數(shù)據(jù)庫(以 .realm 結(jié)尾),因此我們無需頭疼如何去查看 Realm 專有數(shù)據(jù)庫的邏輯結(jié)構(gòu)以及其中的數(shù)據(jù),可視化的操作就如同 SQLite 的其他數(shù)據(jù)庫查看工具一樣,十分簡單、易用(雖然 Realm Browser 的功能還十分簡陋,真的只能讀寫而已)。

Realm數(shù)據(jù)庫基礎(chǔ)教程

Realm Browser

Realm Browser 可以在解壓的 Realm 文件夾中的 browser 文件夾中找到。您也可以訪問 Realm GitHub repository 然后在其中的 tools/RealmBrowser 目錄中找到它。

您可以嘗試在 Realm Browser 中選擇 Tools -> Generate demo database 來試著探索一下 Realm Browser 的功能。

Realm 相關(guān)術(shù)語和主要類

為了幫助您更好地理解 Realm 的使用,下面我們將會對 Realm 的相關(guān)術(shù)語和主要類進行一個大致的介紹:

  • RLMRealm : RLMRealm 是框架的核心所在,是我們構(gòu)建數(shù)據(jù)庫的訪問點,就如同 Core Data 的管理對象上下文( managed  object context )一樣。出于簡單起見, realm 提供了一個名為 defaultRealm 的單例,在本教程中我們就僅使用這個單例來完成我們所需的功能。當(dāng)然,我們也可以導(dǎo)入外部已經(jīng)編寫好的 realm 數(shù)據(jù)庫文件,也可以在我們不需要將數(shù)據(jù)保存在硬盤上時使用 “ 內(nèi)存實例對象 ” ( in-memory realm instance ),此外,還可以同時使用多個數(shù)據(jù)庫文件。

  • RLMObject :這是我們自定義的 realm 數(shù)據(jù)模型。創(chuàng)建數(shù)據(jù)模型的行為將會影響到數(shù)據(jù)庫的結(jié)構(gòu)。要創(chuàng)建一個數(shù)據(jù)模型,我們只需要繼承 RLMObject ,然后設(shè)計我們想要存儲的屬性即可。

  • 關(guān)系 (Relationships) :通過簡單地在數(shù)據(jù)模型中聲明一個 RLMObject 類型的屬性,我們就可以創(chuàng)建一個 “ 一對多 ” 的對象關(guān)系。同樣地,借助 RLMArray 我們還可以創(chuàng)建 “ 多對一 ” 和 “ 多對多 ” 的關(guān)系。

  • 寫操作事務(wù) (Write Transactions) :數(shù)據(jù)庫中的所有操作,比如創(chuàng)建、編輯,或者刪除對象,都必須在事務(wù)中完成。 “ 事務(wù) ” 是指位于 beginWriteTransaction() 以及 commitWriteTransaction() 操作之間的代碼段。

  • 查詢 (Queries) :要在數(shù)據(jù)庫中檢索信息,我們需要用到 “ 檢索 ” 操作。檢索最簡單的形式是對 RLMObject 對象發(fā)送 allObjects() 消息。如果需要檢索更復(fù)雜的數(shù)據(jù),那么還可以使用斷言( predicates )、復(fù)合查詢以及結(jié)果排序等等操作。

  • RLMResults :這個類是執(zhí)行任何查詢請求后所返回的類,其中包含了一系列的 RLMObjects 對象。和 NSArray 類似,我們可以用下標(biāo)語法來對其進行訪問,并且還可以決定它們之間的關(guān)系。不僅如此,它還擁有許多更強大的功能,包括排序、查找等等操作。

現(xiàn)在您應(yīng)該對 Realm 有了一個大概的了解了,現(xiàn)在是時候來試著使用 Realm 來完成起始項目的剩余工作了。

創(chuàng)建第一個數(shù)據(jù)模型

好了,前面我們廢話了這么多,現(xiàn)在終于要開始使用數(shù)據(jù)庫了。首先我們要創(chuàng)建一個數(shù)據(jù)模型,也相當(dāng)于創(chuàng)建數(shù)據(jù)庫的一個 “ 表 ” 。

右鍵選擇 Xcode 項目導(dǎo)航器中的 Model 組,然后選擇 New File -> iOS -> Source -> Swift File ,創(chuàng)建一個新的 swift 文件,將其命名為 SpeciesModel 并且確保選中了 SISpeciesNotes 對象。

提示:您也許查看過 Realm 的開發(fā)文檔,它里面介紹說可以使用 “ 插件 ” 來完成數(shù)據(jù)模型的簡單創(chuàng)建(也就是新建文件時,可以像新建 Core Data 數(shù)據(jù)模型文件一樣創(chuàng)建一個既定的模板數(shù)據(jù)模型),但是很遺憾的是,現(xiàn)在這個功能還只支持創(chuàng)建 OC 版本的數(shù)據(jù)模型文件,我們?yōu)榱舜a的 “ 干凈 ” ,就不采用這種方法。

打開 SpeciesModel.swift 文件,然后用以下代碼替換文件中的內(nèi)容:

import UIKit
import Realm
class SpeciesModel: RLMObject {
dynamic var name = ""
dynamic var speciesDescription = ""
dynamic var latitude: Double = 0
dynamic var longitude: Double = 0
dynamic var created = NSDate()
}

上面的代碼添加了一些屬性來存儲信息: name 屬性存儲物種名稱, speciesDescription 存儲物種的描述信息。對于 Realm 中的一些特定的數(shù)據(jù)類型,比如說字符串,必須要初始化。在本例中,我們使用空字符串來進行初始化。

latitude 以及 longitude 存儲了物種的經(jīng)緯度信息。在這里我們將其類型設(shè)置為 Double ( CLLocationDegrees 是 Double 的別名),并且使用 0 來進行初始化。

最后, created 存儲了這個物種所創(chuàng)建的時間信息。 NSDate() 將會返回當(dāng)前時間,因此我們就用這個值來初始化這個屬性

好了,現(xiàn)在我們就成功創(chuàng)建了第一個 Realm 數(shù)據(jù)模型了,要不要動動腦來完成一個小小的挑戰(zhàn)呢?

我們知道,這些物種將會被劃分為不同的 “ 類別 ” ,您的任務(wù)就是自行創(chuàng)建一個 “ 類別 ” 數(shù)據(jù)模型,這個文件將被命名為 CategoryModel.swift ,然后這個新的數(shù)據(jù)模型只要一個字符串類型的屬性 ——name 。

以下是解決方案的代碼:

import UIKit
import Realm
class CategoryModel: RLMObject {
dynamic var name = ""
}

我們現(xiàn)在擁有了 CategoryModel 數(shù)據(jù)模型了,下面我們將通過某種方式將其與 SpeciesModel 數(shù)據(jù)模型關(guān)聯(lián)起來,搭建起 “ 關(guān)系 ” 。

重新回顧一下上一節(jié)的內(nèi)容,我們可以通過簡單地聲明一個屬性來創(chuàng)建數(shù)據(jù)模型之間的關(guān)系。

打開 SpeciesModel.swift 文件,然后在 created 屬性下面添加如下語句:

dynamic var category = CategoryModel()

這個語句設(shè)置了 “ 物種 ” 和 “ 類別 ” 之間的 “ 一對多 ” 關(guān)系,這就意味著每個物種都只能夠擁有一個類別,但是一個類別可以從屬于多個物種。

好的,我們創(chuàng)建完了一個基礎(chǔ)數(shù)據(jù)模型了,現(xiàn)在是時候向數(shù)據(jù)庫中添加數(shù)據(jù)了!

添加數(shù)據(jù)

每當(dāng)用戶添加了一個新的物種標(biāo)記,用戶就可以對這個標(biāo)記進行修改,比如說設(shè)置物種名字,選擇類別等等。打開CategoriesTableViewController.swift 文件。這個視圖控制器將要在這個表視圖中顯示類別清單,以便用戶可以選擇。

因此,我們需要在應(yīng)用初始運行時,給用戶提供幾個默認的類別以供選擇。

在類定義當(dāng)中添加以下方法,別忘了在文件頂部導(dǎo)入 Realm 框架( import Realm ):

private func populateDefaultCategories() {
self.results = CategoryModel.allObjects() // 1
if results.count == 0 { // 2
let realm = RLMRealm.defaultRealm() // 3
realm.beginWriteTransaction() // 4
let defaultCategories = Categories.allValues // 5
for category in defaultCategories {
// 6
let newCategory = CategoryModel()
newCategory.name = category
realm.addObject(newCategory)
}
realm.commitWriteTransaction() // 7
self.results = CategoryModel.allObjects()
}
}

對應(yīng)的標(biāo)號注釋如下:

  1. allobjects() 方法將會返回指定對象的所有元素,在本例中,我們向數(shù)據(jù)庫中的 CategoryModel 對象發(fā)送了一個查詢請求,返回這個表當(dāng)中的所有行信息。注意的是,這里我們得到的是一個 RLMResults 對象,這個對象用來存放我們的查詢結(jié)果。

  2. 如果查詢結(jié)果中的元素數(shù)量為 0 ,那么就說明數(shù)據(jù)庫當(dāng)中沒有類別信息的相關(guān)記錄,那么就意味著這是用戶第一次啟動應(yīng)用。

  3. 我們訪問默認的 realm 單例對象,然后將其用 realm 變量簡單表示,以供訪問

  4. 這一步將在默認 realm 數(shù)據(jù)庫中啟動一個事務(wù) —— 現(xiàn)在,我們就可以向數(shù)據(jù)庫當(dāng)中添加記錄了。

  5. 這里我們使用已經(jīng)定義過的 Categories 枚舉來創(chuàng)建一個含有全部默認類別的數(shù)組。

  6. 對于每個類別名稱來說,我們創(chuàng)建了一個對應(yīng)的 CategoryModel 實例對象,然后設(shè)置其 name 屬性,最后將這個對象添加到 realm 當(dāng)中。

  7. 當(dāng)我們添加完所有的類別之后,調(diào)用 commitWriteTransaction() 方法來關(guān)閉事務(wù),并且向數(shù)據(jù)庫提交數(shù)據(jù)。

只有調(diào)用了 commitWriteTransaction() 方法,我們之前做的所有關(guān)于事務(wù)的操作才能夠被成功運行,因為這涉及到 Realm 的內(nèi)部處理的問題了。您可以像上面我們做的那樣,執(zhí)行一些簡單的創(chuàng)建操作,或者您可以執(zhí)行一些復(fù)雜的操作,比如說同時創(chuàng)建、更新、刪除多個對象等等。

然后在 viewDidLoad() 方法的底部加入以下代碼:

populateDefaultCategories()

這個方法將會在視圖加載的過程中,添加我們的測試用類別,并且執(zhí)行向數(shù)據(jù)庫寫入數(shù)據(jù)的操作。

好了,現(xiàn)在我們的數(shù)據(jù)庫當(dāng)中已經(jīng)有了一些數(shù)據(jù)了,我們需要更新一下表試圖數(shù)據(jù)源相關(guān)方法,以顯示這些類別。找到 tableView(_:cellForRowAtIndexPath:) 方法,然后用以下代碼替換它:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CategoryCell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = (results[UInt(indexPath.row)] as! CategoryModel).name
return cell
}

這個聲明語句從 results 對象當(dāng)中讀取對應(yīng)行的名稱,然后設(shè)置到單元格的文本標(biāo)簽上面顯示。

接下來,添加一個新的屬性:

var selectedCategory: CategoryModel!

我們用這個屬性來存儲當(dāng)前選中的類別。

找到 tableView(_: willSelectedRowAtIndexPath:) ,然后用以下代碼替換它:

override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
selectedCategories = self.results[UInt(indexPath.row)] as! CategoryModel
return indexPath
}

上面聲明的方法將會在用戶點擊某個單元格的時候,將用戶點擊的類別存儲在 selectedCategory 屬性當(dāng)中。

編譯并運行這個應(yīng)用,然后嘗試定位到某個您感興趣的位置(使用模擬器的位置模擬),然后點擊右上角的 “+” 按鈕創(chuàng)建一個新的標(biāo)記點。點選地圖上的這個標(biāo)記點,然后點擊其彈出來的氣泡,接下來會彈出這個標(biāo)記點的詳細信息。隨后,點擊類別文本框,就可以看到如下圖所示的類別列表了:

Realm數(shù)據(jù)庫基礎(chǔ)教程

類別列表

您可以選擇其中一個類別,不過這個操作僅僅只是將其保存到屬性當(dāng)中。如果您感興趣,可以前往模擬器的 Documents 目錄下面,使用 Realm Browser 查看我們生成的數(shù)據(jù)庫,在里面就可以看到我們寫入的數(shù)據(jù)了,這是不是很令人激動呢?

使用 Realm Browser

通常情況下,使用 defaultRealm() 方法生成的數(shù)據(jù)庫文件將會存放在 /Users/(Your Account)/Library/Developer/CoreSimulator/Devices/(Simulator ID)/data/Containers/Data/Application/(Application ID)/Documents/ 路徑下面,名為 default.realm 。 Simulator ID 指的是您運行的模擬器的 ID , Application ID 指的是這個應(yīng)用所分配到的 ID 。

如果您仍然不清楚這個 Realm 數(shù)據(jù)庫在哪兒的話,那么使用如下語句,就可以打印處這個數(shù)據(jù)庫所在的完整位置了:

println(RLMRealm.defaultRealm().path)

在這個 Documents 目錄下面,我們可能會看到兩個文件。一個是 default.realm 文件,這個是數(shù)據(jù)庫文件,里面就是數(shù)據(jù)的存放點了。而另一個則是 default.realm.lock 文件,這個文件也有可能不會存在,它是用來當(dāng)數(shù)據(jù)庫文件被使用時,防止其它應(yīng)用對其進行修改的一個文件。

雙擊這個 default.realm 文件,就可以使用 Realm Browser 打開了:

Realm數(shù)據(jù)庫基礎(chǔ)教程

Realm Browser 打開的 default.realm 文件

注意:如果 default.realm 已經(jīng)在其它應(yīng)用中打開了,那么強行打開它就可能會出現(xiàn)異常。

.lock 文件就可以防止對 default.realm 文件的重復(fù)操作,在使用 Realm Browser 打開數(shù)據(jù)庫文件前,請先確保應(yīng)用沒有在運行,然后刪除 .lock 文件,才能打開。

一旦數(shù)據(jù)庫在 Realm Browser 中被打開,您將會看到 CategoryModel 類中擁有 6 個對象,這就意味著這個 “ 表 ” 中已經(jīng)存放了 6 個記錄了。點擊這個類就可以查看這個類當(dāng)中擁有的具體對象信息。

增加類別

好了,現(xiàn)在我們就可以來實現(xiàn) “ 為某個物種添加類別 ” 的功能了。

打開 AddNewEntryController.swift ,然后向類中添加以下屬性:

var selectedCategory: CategoryModel!

我們將會用這個屬性來存儲我們在 CategoriesTableViewController 選中的類別。

接下來,找到 unwindFromCategories(segue:) 方法,然后在方法底部添加以下代碼:

selectedCategory = categoriesController.selectedCategories
categoryTextField.text = selectedCategory.name

這個方法會在用戶從 categoriesTableViewController 中選擇了一個類別后被調(diào)用。在這里,我們獲取到了這個選擇的類別,然后將其存儲在本地屬性 selectedCategory 當(dāng)中,接著,我們將它的值填充到文本框里面。

現(xiàn)在,我們已經(jīng)完成了類別的獲取,接下來就是要創(chuàng)建第一個物種了!

仍然還是在 AddNewEntryController.swift 當(dāng)中,向類中再添加一個屬性:

var species: SpeciesModel!

這個屬性將會存儲一個新的物種數(shù)據(jù)模型對象。

接下來,導(dǎo)入 Realm 框架,然后向類中添加以下方法:

func addNewSpecies() {
let realm = RLMRealm.defaultRealm() // 1
realm.beginWriteTransaction() // 2
let newSpecies = SpeciesModel() // 3
// 4
newSpecies.name = nameTextField.text
newSpecies.category = selectedCategory
newSpecies.speciesDescription = descriptionTextView.text
newSpecies.latitude = selectedAnnotation.coordinate.latitude
newSpecies.longitude = selectedAnnotation.coordinate.longitude
realm.addObject(newSpecies) // 5
realm.commitWriteTransaction() // 6
self.species = newSpecies
}

對應(yīng)的標(biāo)號注釋如下:

  1. 獲取默認的 Realm 數(shù)據(jù)庫

  2. 開啟一個事務(wù)序列,準(zhǔn)備寫入數(shù)據(jù)

  3. 創(chuàng)建一個 Species 對象實例

  4. 接著,設(shè)置這個對象的相關(guān)值。這些值來自于用戶界面的文本輸入框。

  5. 向 realm 中寫入新的 Species 對象

  6. 最后,使用 commitWriteTransaction() 提交寫操作事務(wù)

在這里,我們需要使用 “ 輸入驗證 ” ,來確保用戶的輸入是正確的。在工程中已經(jīng)有了一個存在的 validateFields() 方法來執(zhí)行輸入驗證的工作,以確保物種名稱和描述不能為空。我們剛剛增加了設(shè)置類別的功能,那么我們應(yīng)該也要確保類別選擇不能為空。

在 validateFields() 方法中找到以下代碼:

if nameTextField.text.isEmpty || descriptionTextView.text.isEmpty {
將其變更為:
if nameTextField.text.isEmpty || descriptionTextView.text.isEmpty || selectedCategory == nil {

這個方法經(jīng)能夠確保所有的文本框都有值,并且用戶也已經(jīng)選擇了一個類別。

接下來,向類中添加以下方法:

override func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool {
if validateFields() {
if species == nil {
addNewSpecies()
}
return true
} else {
return false
}
}

在上面的代碼中,我們調(diào)用了輸入驗證的方法,如果所有文本框都有值的話,那么就可以添加一個新的物種。

編譯并運行您的應(yīng)用,單擊 “+” 按鈕來創(chuàng)建一個新的物種。然后輸入其名稱和描述,選擇一個類別,接著單擊 “ 保存 ” 按鈕來將這個物種添加到數(shù)據(jù)庫中。

Realm數(shù)據(jù)庫基礎(chǔ)教程

添加新的數(shù)據(jù)

視圖消失了 —— 等等,怎么什么都沒有發(fā)生呢?什么情況?

哦對了,我們已經(jīng)向 Realm 數(shù)據(jù)庫提交了一個數(shù)據(jù),但是我們還沒有在地圖上做出相應(yīng)的設(shè)置和改變。

檢索數(shù)據(jù)

既然我們已經(jīng)向數(shù)據(jù)庫中添加了一個物種了,那么現(xiàn)在我們希望它能夠在地圖上顯示出來。

如果您想要檢視這個心數(shù)據(jù),那么打開 Realm Browser 就可以查看數(shù)據(jù)了。記住要先退出模擬器。

Realm數(shù)據(jù)庫基礎(chǔ)教程

添加的物種信息

我們僅僅只能夠看見孤零零的一條記錄,里面存儲了記錄的名稱、描述信息、經(jīng)緯度信息、添加的時間。還有最重要的,就是我們看到了連接到 CategoryModel 的 category 記錄,這就意味著我們已經(jīng)創(chuàng)建好了物種和類別的 “ 一對多 ” 關(guān)系。點擊這個藍色的超鏈接,我們就可以查看 CategoryModel 的相關(guān)數(shù)據(jù)了。

好的,回到正題,我們現(xiàn)在需要在地圖上顯示新添加的數(shù)據(jù)。

打開 SpeciesAnnotation.swift ,然后向類中添加一個新的屬性:

var species: SpeciesModel?

這個屬性將會為這個標(biāo)記點保存它所擁有的物種信息。

接下來,用以下代碼替換構(gòu)造器:

init(coordinate: CLLocationCoordinate2D, title: String, sub: Categories, species: SpeciesModel? = nil) {
self.coordinate = coordinate
self.title = title
self.subtitle = sub.rawValue
self.species = species
}

我們所做的改變,就是給這個構(gòu)造器方法添加了一個帶默認值的構(gòu)造器參數(shù),以便可以對 species 屬性進行賦值。默認值為 nil ,這意味著我們可以忽略這個參數(shù),使用前面三個參數(shù)進行初始化也是沒有任何問題的。

打開 MapViewController.swift ,然后向類中添加一個新屬性(同樣地,別忘了導(dǎo)入 Realm ):

var results: RLMResults?

如果我們想要在用屬性來存儲一系列物種,那么我們需要將這個屬性聲明為 RLMResults 類型。要記住,我們是不能夠初始化 RLMResults 對象的,我們必須要通過查詢操作來獲取它的值。

現(xiàn)在我們需要一些方法來獲取所有的物種數(shù)據(jù)。仍然還是在 MapViewController.swift 當(dāng)中,向類中添加如下方法:

func populateMap() {
mapView.removeAnnotations(mapView.annotations) // 1
if let results = SpeciesModel.allObjects() { // 2
self.results = results
for result in results {
let species = result as! SpeciesModel
let coordinate = CLLocationCoordinate2DMake(species.latitude, species.longitude)
let speciesAnnotation = SpeciesAnnotation(coordinate: coordinate, title: species.name, sub: Categories(rawValue: species.category.name)!, species: species) // 3
mapView.addAnnotation(speciesAnnotation) // 4
}
}
}

對應(yīng)的標(biāo)號注釋如下:

  1. 首先,我們先清除了地圖上所有存在的標(biāo)記點,這樣我們就不用考慮其他的要素

  2. 然后,我們從 Realm 數(shù)據(jù)庫中獲取 Species 的全部數(shù)據(jù)

  3. 我們在此創(chuàng)建了一個自定義的 SpeciesAnnotation

  4. 最后,我們往 MKMapView 上添加這個標(biāo)記點

好的,現(xiàn)在我們可以在某處地方吊用這個方法了。找到 viewDidLoad() 然后將這個方法加入到這個方法底部:

populateMap()

這樣就確保了每當(dāng)?shù)貓D視圖控制器加載的時候,地圖就能夠顯示 Species 標(biāo)記點。

接著,我們僅需要修改標(biāo)記點的名稱和類別即可。找到 unwindFromAddNewEntry() ,然后使用下列代碼替換掉該方法:

@IBAction func unwindFromAddNewEntry(segue: UIStoryboardSegue) {
let addNewEntryController = segue.sourceViewController as! AddNewEntryController
let addedSpecies = addNewEntryController.species
let addedSpeciesCoordinate = CLLocationCoordinate2DMake(addedSpecies.latitude, addedSpecies.longitude)
if lastAnnotation != nil {
mapView.removeAnnotation(lastAnnotation)
} else {
for annotation in mapView.annotations {
let currentAnnotation = annotation as! SpeciesAnnotation
if currentAnnotation.coordinate.latitude == addedSpeciesCoordinate.latitude && currentAnnotation.coordinate.longitude == addedSpeciesCoordinate.longitude {
mapView.removeAnnotation(currentAnnotation)
break
}
}
}
let annotation = SpeciesAnnotation(coordinate: addedSpeciesCoordinate, title: addedSpecies.name, sub: Categories(rawValue: addedSpecies.category.name)!, species: addedSpecies)
mapView.addAnnotation(annotation)
lastAnnotation = nil
}

這個方法將會在我們從 AddNewEntryController 返回的時候被調(diào)用,然后這時候就會有一個新的物種被添加到地圖上方。當(dāng)我們添加了一個新的物種到地圖上,那么就會產(chǎn)生一個標(biāo)記圖標(biāo)。然后我們想要根據(jù)物種的類別來改變其圖標(biāo)的樣式,在這個代碼里面,我們就是簡單的移除了最后添加的這個標(biāo)記點,然后將其替換為有名稱和類別的標(biāo)記點。

編譯并運行您的應(yīng)用,創(chuàng)建一些不同的物種種類來查看現(xiàn)在地圖是什么樣式的吧!

Realm數(shù)據(jù)庫基礎(chǔ)教程

添加的標(biāo)記點效果

另外一個視圖

您或許已經(jīng)注意到在地圖視圖的左上角有一個 “ 編輯 ” 的按鈕。為了更好地管理地圖上的記錄點,我們這個應(yīng)用設(shè)置了一個基于文本的表視圖,用來列出地圖上所有的記錄點,這個視圖我們現(xiàn)在命名為 “ 記錄 ” 視圖?,F(xiàn)在,這個表視圖仍然還是空的,現(xiàn)在我們就來向里面填充數(shù)據(jù)吧!

打開 LogViewController.swift ,然后將 species 屬性替換成以下形式(同樣地,要導(dǎo)入 Realm ):

var species: RLMResults!

在上面的代碼中,我們用 RLMResults 替換掉了之前的一個空數(shù)組占位符,這個操作和我們在 MapViewController 所做的一樣。

接下來,找到 viewDidLoad() 方法,然后在 super.viewDidLoad() 語句下添加以下代碼:

species = SpeciesModel.allObjects().sortedResultsUsingProperty("name", ascending: true)

這行代碼會將數(shù)據(jù)庫中的所有物種全部輸出到 species 當(dāng)中,并且按照名字進行排列。

接下來,用以下代碼替換 tableView(_:cellForRowAtIndexPath:) :

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("LogCell") as! LogCell
var speciesModel: SpeciesModel!
speciesModel = species[UInt(indexPath.row)] as! SpeciesModel
cell.titleLabel.text = speciesModel.name
cell.subtitleLabel.text = speciesModel.category.name
cell.iconImageView.image = getImageOfSpecies(speciesModel.category.name)
return cell
}

這個方法將會展示物種的名字和物種的類別,以及其圖標(biāo)。

編譯并運行應(yīng)用,單擊左上角的 “ 編輯 ” 按鈕,然后您就會在表視圖中看到我們之前錄入的物種信息,如圖所示:

Realm數(shù)據(jù)庫基礎(chǔ)教程

記錄界面

刪除記錄

現(xiàn)在我們已經(jīng)學(xué)習(xí)了如何在 Realm 中創(chuàng)建記錄數(shù)據(jù),但是如果我們不小心添加了錯誤的標(biāo)記點,或者想要移除之前添加過的物種數(shù)據(jù),那么我們應(yīng)該要怎么做呢?因此,我們就需要添加從 Realm 中刪除數(shù)據(jù)的功能。您會發(fā)現(xiàn)這是一個非常簡單的操作。

打開 LogViewController.swift 文件,然后添加以下方法:

func deleteRowAtIndexPath(indexPath: NSIndexPath) {
let realm = RLMRealm.defaultRealm() // 1
let objectToDelete = species[UInt(indexPath.row)] as! SpeciesModel // 2
realm.beginWriteTransaction() // 3
realm.deleteObject(objectToDelete) // 4
realm.commitWriteTransaction() // 5
species = SpeciesModel.allObjects().sortedResultsUsingProperty("name", ascending: true) // 6
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) // 7
}

對應(yīng)的標(biāo)號注釋如下:

  1. 首先,我們獲取到默認的 Realm 數(shù)據(jù)庫

  2. 然后,我們從數(shù)據(jù)中找到我們想要刪除的對象

  3. 啟動寫操作事務(wù)

  4. 調(diào)用 deleteObject() 方法,將要刪除的對象傳遞進去, realm 會自動幫我們執(zhí)行刪除操作

  5. 接著提交寫操作事務(wù),將刪除操作提交到數(shù)據(jù)庫當(dāng)中

  6. 一旦我們移除了一個物種,我們需要重新讀取數(shù)據(jù)

  7. 最后,我們更新 UITableViewCell ,將單元格移除

接著,找到 tableView(_:commitEditingStyle: forRowAtIndexPath:) 方法,然后將以下代碼加入到 if 語句塊當(dāng)中:

deleteRowAtIndexPath(indexPath)

當(dāng)表視圖執(zhí)行一個單例刪除操作時,會調(diào)用這個協(xié)議代理,我們所需要做的就是調(diào)用我們剛剛創(chuàng)建的那個方法。

編譯并運行您的應(yīng)用,查看 “ 記錄 ” 界面,然后在某個記錄上面左滑刪除。隨后關(guān)閉模擬器,用 Realm Browser 打開數(shù)據(jù)庫,我們就可以看到我們成功執(zhí)行了更改:

Realm數(shù)據(jù)庫基礎(chǔ)教程

執(zhí)行刪除操作

斷言匹配

我們?nèi)匀贿€想要給這個應(yīng)用提供一些碉堡的功能,那么快速查找怎么樣?在海量的數(shù)據(jù)中進行查找還是很麻煩的一件事情,但是有了快速查找,一切就都簡單了。我們現(xiàn)在所擁有的這個項目已經(jīng)包含了一個 UISearchController 控件,您所需要做的就是添加一點小小的修改,讓這個功能能夠在 Realm 中正常工作。

打開 LogViewController.swift ,然后將 searchResults 屬性替換為以下代碼:

var searchResults: RLMResults!

因為我們?nèi)匀皇菆?zhí)行 “ 檢索 ” 操作,因此我們的數(shù)據(jù)是存放在 RLMResults 當(dāng)中的。

向類中添加以下方法:

func filterResultsWithSearchString(searchString: String) {
let predicate = "name BEGINSWITH [c]'\(searchString)'" // 1
let scopeIndex = searchController.searchBar.selectedScopeButtonIndex
searchResults = SpeciesModel.objectsWhere(predicate) // 2
switch scopeIndex {
case 0:
searchResults = searchResults.sortedResultsUsingProperty("name", ascending: true) // 3
case 1:
searchResults = searchResults.sortedResultsUsingProperty("distance", ascending: true) // 4
case 2:
searchResults = searchResults.sortedResultsUsingProperty("created", ascending: true) 5
default:
return
}
}

對應(yīng)的標(biāo)號注釋如下:

  1. 首先我們創(chuàng)建了一個字符串版本的 “ 斷言 (predicate)” ,在這里,我們搜索以 searchString 開頭的 name 屬性。 [c] 可以讓 BEGINSWITH 以不區(qū)分大小寫的靈敏度來進行查找,要注意, searchString 是被單引號括起來的。

  2. 我們根據(jù)這個斷言,使用 objectsWhere 這個方法來執(zhí)行斷言檢索操作。

  3. 如果選中的標(biāo)簽是 “ 名字 ” ,那么結(jié)果就按照 “ 名字 A-Z” 排列

  4. 如果選中的標(biāo)簽是 “ 距離 ” ,那么就按照距離排列結(jié)果

  5. 如果選中的標(biāo)簽是 “ 創(chuàng)建時間 ” ,那么就按照時間來進行排列。

因為搜索會導(dǎo)致表視圖調(diào)用同樣的數(shù)據(jù)源方法,因此我們需要對 tableView(_:cellForRowAtIndexPath:) 進行小小的修改,以便讓其能夠處理主要的表視圖記錄以及查詢結(jié)果。在這個方法里面,找到以下代碼:

speciesModel = species[UInt(indexPath.row)] as! SpeciesModel
將其替換為以下代碼:
if searchController.active {
speciesModel = searchResults[UInt(indexPath.row)] as! SpeciesModel
}else {
speciesModel = species[UInt(indexPath.row)] as! SpeciesModel
}

上面這行代碼將會檢查 searchController 是否激活。如果激活的話,那么就接收并顯示搜索結(jié)果的數(shù)據(jù);如果不是的話,那么就接收并顯示 species 全部數(shù)據(jù)。

最后,我們需要一個功能,那就是單擊范圍欄上的按鈕時,更變返回結(jié)果的排列順序。

將空 scopeChanged 方法用以下代碼來替換:

@IBAction func scopeChanged(sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
species = SpeciesModel.allObjects().sortedResultsUsingProperty("name", ascending: true)
case 1:
break
case 2:
species = SpeciesModel.allObjects().sortedResultsUsingProperty("created", ascending: true)
default:
species = SpeciesModel.allObjects().sortedResultsUsingProperty("name", ascending: true)
}
tableView.reloadData()
}

在上面的代碼中,我們將會檢查范圍欄上的按鈕是哪一個被按下( A-Z ,距離,以及添加日期),然后調(diào)用 sortedResultsUsingProperty 來進行排序。通常情況下,這個列表將按照名字來排序。

您可能會注意到,現(xiàn)在按照距離排序這一塊中目前還是空的( case 1 ),那是因為目前數(shù)據(jù)模型中還不存在 “ 距離 ” 這么一個玩意兒,因此我們暫時還不需要做這個工作,等到以后添加了再來完善。不過現(xiàn)在,我們已經(jīng)可以看到它的大致功能了!

編譯并運行您的應(yīng)用,嘗試一些搜索操作,然后查看結(jié)果!

Realm數(shù)據(jù)庫基礎(chǔ)教程

查看不同的結(jié)果

提示:在作者的原教程中,搜索功能實際上是無法實現(xiàn)的。如果您在 “ 合適 ” 的地方添加了相關(guān)方法,那么實際上程序仍然還是無法執(zhí)行搜索功能的。它會提示 cell 的 titleLabel 的值為 nil 。因為在原教程中, Cell 是在 Storyboard 里面自定義的,而搜索欄則是要顯示一個新的表視圖。如果需要重用自定義的 Cell ,那么最好需要在 Xib 文件中進行制作。因為如果沒有 init(style:reuseIdentifier:) 方法的 Cell 自定義類,是無法進行重用的。

更新記錄

我們現(xiàn)在已經(jīng)實現(xiàn)了添加和刪除記錄的功能了,剩下就是更新數(shù)據(jù)功能了。

如果您試著單擊 LogViewController 中的一個單元格,那么就會跳轉(zhuǎn)到 AddNewEntryViewController 頁面,但是這些區(qū)域都是空白的。當(dāng)然,我們首先要做的就是讓這個頁面顯示數(shù)據(jù)庫中存放的數(shù)據(jù),以便讓用戶編輯。

打開 AddNewEntryViewController.swift 文件,然后向類中添加以下方法:

func fillTextFields() {
nameTextField.text = species.name
categoryTextField.text = species.category.name
descriptionTextView.text = species.speciesDescription
selectedCategory = species.category
}

這個方法將會使用 species 中的數(shù)據(jù)來填充用戶界面的文本框。記住, AddNewEntryViewController 只有在添加新物種時才會保持文本框為空的狀態(tài)。

接下來,向 viewDidLoad() 方法的末尾添加以下語句:

if species == nil {
title = " 添加新的物種 "
}else {
title = " 編輯 \(species.name)"
fillTextFields()
}

上面這些代碼段設(shè)置了導(dǎo)航欄的標(biāo)題,以通知用戶當(dāng)前其是在添加新的物種還是在更新一個已存在的物種信息。如果 species 不為空,那么就調(diào)用 fillTextFields 方法來填充文本框。

現(xiàn)在我們需要一個更新功能,以便響應(yīng)用戶的更改操作。向類中添加以下方法:

func updateSpecies() {
let realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
species.name = nameTextField.text
species.category = selectedCategory
species.speciesDescription = descriptionTextView.text
realm.commitWriteTransaction()
}

通常情況下,這種方法一般都先獲得默認的 Realm 數(shù)據(jù)庫,然后將數(shù)據(jù)寫入的操作放在 beginWriteTransaction() 和 commitWriteTransaction() 方法之間。在這個事務(wù)中,我們只是簡單的更新了這三個數(shù)據(jù)域的值。

這六行短短的代碼就足以完成 Species 記錄的更新操作了哦 ~O(∩_∩)O~

現(xiàn)在我們只需要在用戶單擊保存按鈕的時候調(diào)用上述代碼即可。找到 shouldPerformSegueWithIdentifier(_:sender:) ,然后在 return true 語句之前,第一個 if 代碼塊之內(nèi)添加以下代碼:

else {
updateSpecies()
}

當(dāng)恰當(dāng)?shù)臅r候,就會調(diào)用這個方法來對數(shù)據(jù)進行更新。

現(xiàn)在打開 LogViewController.swift ,然后將 prepareForSegue(_:sender:) 用以下代碼替換:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Edit" {
let controller = segue.destinationViewController as! AddNewEntryController
var selectedSpecies: SpeciesModel!
let indexPath = tableView.indexPathForSelectedRow()
if searchController.active {
let searchResultsController = searchController.searchResultsController as! UITableViewController
let indexPathSearch = searchResultsController.tableView.indexPathForSelectedRow()
selectedSpecies = searchResults[UInt(indexPathSearch!.row)] as! SpeciesModel
}else{
selectedSpecies = species[UInt(indexPath!.row)] as! SpeciesModel
}
controller.species = selectedSpecies
}
}

我們在這里將選中的物種信息傳遞給了 AddNewEntryController 。上面的 if/else 代碼是因為要根據(jù)用戶是否是在查看搜索結(jié)果來決定的。

編譯并運行您的應(yīng)用,打開記錄視圖,并且選中一個存在的物種。您應(yīng)該可以看到文本框中已經(jīng)填充了數(shù)據(jù)。

Realm數(shù)據(jù)庫基礎(chǔ)教程

更新數(shù)據(jù)

把剩下的東西結(jié)束掉

要讓我們的應(yīng)用變得更加完美,我們就需要實現(xiàn)剩下 的功能。

還記不記得我們沒有變法根據(jù)距離來排序記錄?我們需要在里面添加不少的代碼,才能夠正常的運行這個功能,但是這個結(jié)果是非常值得的。

打開 Species.swift 文件,然后向類中添加一個新的屬性。

dynamic var distance: Double = 0

這個屬性為保存用戶位置和該記錄點的距離信息。然而,沒有必要去存儲 distance 信息,因為用戶位置會隨時發(fā)生改變。我們想讓距離成為這個模型的一部分,但是我們并不想 Realm 來存儲這個數(shù)據(jù)。

Realm 支持一種被稱為忽視屬性 (ignored properties) 的東西,然后向類中添加以下代碼:

func ignoredProperties() -> NSArray {
let propertiesToIgnore = [distance]
return propertiesToIgnore
}

要實現(xiàn)忽視屬性,只需要聲明一個命名為 ignoredProperties() 的方法,然后返回一個屬性數(shù)組,里面保存有您不想進行存儲的屬性。

由于我們并不會存儲距離這個屬性,很明顯地我們需要自己計算距離。

打開 MapViewController.swift ,添加以下方法:

func updateLocationDistance() {
let realm = RLMRealm.defaultRealm()
if results != nil {
for result in results! {
let currentSpecies = result as! SpeciesModel
let currentLocation = CLLocation(latitude: currentSpecies.latitude, longitude: currentSpecies.longitude)
let distance = currentLocation.distanceFromLocation(mapView.userLocation.location)
realm.beginWriteTransaction()
currentSpecies.distance = Double(distance)
realm.commitWriteTransaction()
}
}
}

對于每個物種,我們計算了這個標(biāo)記點與用戶當(dāng)前位置之間的距離。即時我們沒有存儲這個距離信息,我們?nèi)匀恍枰獙⑵浯鎯υ谟涗洰?dāng)中,然后將其在寫操作事務(wù)中保存這個變化消息。

接下來,在 prepareForSegue(_:sender:) 方法底部添加以下代碼:

else if segue.identifier == "Log" {
updateLocationDistance()
}

現(xiàn)在,在用戶打開 “ 記錄界面 ” 之前,我們需要調(diào)用這個方法來計算距離。

接下來,打開 LogViewController.swift ,然后找到 tableView(_:cellForRowAtIndexPath:) 方法。然后在這個方法底部附近, return 語句之前添加以下代碼:

if speciesModel.distance < 0 {
cell.distanceLabel.text = "N/A"
}else {
cell.distanceLabel.text = String(format: "%.2fkm", speciesModel.distance / 1000)
}
最后,找到 scopeChanged() 然后將 case 1 中的 break 替換成以下代碼:
species = SpeciesModel.allObjects().sortedResultsUsingProperty("distance", ascending: true)
編譯并運行應(yīng)用,然后 …… 呃?怎么崩潰掉了?
'RLMException`, reason: 'Column count does not match interface - migration required'

什么鬼?

當(dāng)我們向 Species 模型中添加了一個新的 distance 屬性的時候,我們就對架構(gòu)( schema ) 進行了變更,但是我們并沒有告訴 Realm 如何處理這個新增的數(shù)據(jù)段。從舊版本的數(shù)據(jù)庫遷移( migrate ) 到新版本的數(shù)據(jù)庫的操作超出了本教程的范圍。這并不是 Realm 獨有的問題, Core Data 同樣也需要在添加、變更或者刪除新的數(shù)據(jù)段的時候進行遷移操作。

本教程的簡單解決方案就是將模擬器的應(yīng)用移除掉即可,然后重新編譯并運行應(yīng)用程序。這將會讓應(yīng)用創(chuàng)建一個全新的數(shù)據(jù)庫,使用新的架構(gòu)。

從模擬器刪除這個應(yīng)用,接下來編譯和運行這個應(yīng)用。然后添加新的物種,接著打開這個記錄視圖,這時候我們就可以看到如下所示的距離信息:

Realm數(shù)據(jù)庫基礎(chǔ)教程

距離信息

您或許需要模擬一個位置以便能夠計算當(dāng)前距離,在模擬器菜單欄上,選擇 Debug\Location ,然后選擇列表中的一個位置模擬。

接下來該何去何從?

您可以點擊此處下載完整的項目

在本教程中,我們學(xué)習(xí)了如何創(chuàng)建、更新、刪除以及查找 Realm 數(shù)據(jù)庫中的數(shù)據(jù)記錄,以及如何使用斷言來進行查找,還有按屬性名對結(jié)果進行排序的方法。

您可能要問了: “ 看起來 Realm 似乎是一個新項目,我感覺在一個完備的應(yīng)用中使用它可能并不穩(wěn)定 ” 。

Realm 最近才想公眾開放,但是早在 2012 年它就已經(jīng)在公司級別的產(chǎn)品中使用了。我個人已經(jīng)在我的既有項目中使用 Realm 了,而且似乎運轉(zhuǎn)起來相當(dāng)不錯。

如果您使用 Objective-C ,那么 Realm 是十分穩(wěn)定的。對于 Swift 來說,由于 Swift 版本并不穩(wěn)定,因此在使用 Realm 可能會遭遇到版本更迭所引發(fā)的一系列語法問題。不過隨著 Swift 的更新,相信 Swift 的版本改動會越來越少, Realm 在 Swift 上也會越來越穩(wěn)定。

對于 Realm 來說,它還有許多在本教程沒有介紹到的特點:

  • 遷移( Migrations ) :在本教程中,我們看到了對 Realm 架構(gòu)的修改導(dǎo)致了錯誤的產(chǎn)生。要學(xué)習(xí)關(guān)于如何在多版本之間遷移數(shù)據(jù)庫的只是,請查看 Realm 說明文檔的 “ 遷移( migrations) ” 部分。

  • 其他類型的 Realm 數(shù)據(jù)庫 :在本教程中,我們一直都是使用著 “ 默認 ”Realm 數(shù)據(jù)庫,但是我們?nèi)匀贿€可以使用其他類型的 Realm 數(shù)據(jù)庫,比如說不存儲數(shù)據(jù)的 “ 內(nèi)存數(shù)據(jù)庫( in-memory realm ) ” 。我們也可以使用多個 Realm 數(shù)據(jù)庫,如果我們享用多個數(shù)據(jù)庫來保存多種類型的數(shù)據(jù)的話。

關(guān)于 Realm 的更多信息,您可以查看 官方文檔 ,我發(fā)現(xiàn)這個文檔真的寫得十分詳盡。

如果您對本教程有什么建議和意見,請到評論區(qū)進行討論,我會盡快處理這些建議和意見的 ~

原文 Realm Tutorial

原文作者 Bill Kastanakis

譯者及改編 星夜暮晨(QQ:412027805)


您還未登錄,請先登錄

熱門帖子

最新帖子

?