swift開發(fā)需要應(yīng)用數(shù)據(jù)庫,fmdb筆記繁瑣,在swift中準(zhǔn)備放棄使用,看到了 Realm這個三方框架很強大,而且使用簡單,就拿過來研究一下,感謝Swift - Realm數(shù)據(jù)庫的使用詳解(附樣例) 這篇文章,結(jié)合Realm官網(wǎng)知識Realm官網(wǎng)知識及GitHub的demoRealmCooa,可以熟練使用,以此做個記錄,以防下次遺忘
1、什么是RealmRealm
于2014 年7月發(fā)布,是一個跨平臺的移動數(shù)據(jù)庫引擎,專門為移動應(yīng)用的數(shù)據(jù)持久化而生。其目的是要取代 Core Data 和 SQLite。
2、關(guān)于Realm,你要知道下面幾點:
(1)使用簡單,大部分常用的功能(比如插入、查詢等)都可以用一行簡單的代碼輕松完成,學(xué)習(xí)成本低。
(2)Realm 不是基于 Core Data,也不是基于 SQLite 封裝構(gòu)建的。它有自己的數(shù)據(jù)庫存儲引擎。
(3)Realm 具有良好的跨平臺特性,可以在 iOS 和 Android 平臺上共同使用。代碼可以使用 Swift 、 Objective-C 以及 Java 語言來編寫。
(4)Realm 還提供了一個輕量級的數(shù)據(jù)庫查看工具(Realm Browser)。你也可以用它進(jìn)行一些簡單的編輯操作(比如插入和刪除操作)
3、支持的類型
(1)Realm 支持以下的屬性類型:Bool、Int8、Int16、Int32、Int64、Double、Float、String、Date(精度到秒)以及Data.
(2)也可以使用 List<object> 和 Object 來建立諸如一對多、一對一之類的關(guān)系模型,此外 Object 的子類也支持此功能。
4、Realm的安裝配置
(1)先去 Realm 的官網(wǎng)去下載最新框架:http://static.realm.io/downloads/swift/latest
(2)拖拽 RealmSwift.framework 和 Realm.framework 文件到”Embedded Binaries”選項中。選中 Copy items if needed 并點擊 Finish

5、將數(shù)據(jù)插入到數(shù)據(jù)庫中
下面代碼判斷默認(rèn)數(shù)據(jù)庫中是否有數(shù)據(jù),如果沒有的話將幾個自定義對像插入到數(shù)據(jù)庫中。
(1)這里以個人消費記錄為例,我們先定義消費類別類,和具體消費記錄類
import Foundation
import RealmSwift
//消費類型
class ConsumeType:Object {
//類型名
dynamic var name = ""
}
//消費條目
class ConsumeItem:Object {
//條目名
dynamic var name = ""
//金額
dynamic var cost = 0.00
//時間
dynamic var date = Date()
//所屬消費類別
dynamic var type:ConsumeType?
}
(2)判斷數(shù)據(jù)庫記錄是否為空,空的話則插入數(shù)據(jù)庫(這里以默認(rèn)數(shù)據(jù)庫為例)
import UIKit
import RealmSwift
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//使用默認(rèn)的數(shù)據(jù)庫
let realm = try! Realm()
//查詢所有的消費記錄
let items = realm.objects(ConsumeItem.self)
//已經(jīng)有記錄的話就不插入了
if items.count>0 {
return
}
//創(chuàng)建兩個消費類型
let type1 = ConsumeType()
type1.name = "購物"
let type2 = ConsumeType()
type2.name = "娛樂"
//創(chuàng)建三個消費記錄
let item1 = ConsumeItem(value: ["買一臺電腦",5999.00,Date(),type1]) //可使用數(shù)組創(chuàng)建
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
// 數(shù)據(jù)持久化操作(類型記錄也會自動添加的)
try! realm.write {
realm.add(item1)
realm.add(item2)
realm.add(item3)
}
//打印出數(shù)據(jù)庫地址
print(realm.configuration.fileURL)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
6、Data類型數(shù)據(jù)的存取
1.實現(xiàn)原理
(1)Realm 支持 Data 類型的屬性,我們要做的就是將圖片轉(zhuǎn)換為 Data 類型,再進(jìn)行存儲即可。
(2)Data 類型的屬性讀取操作同其他數(shù)據(jù)類型的讀取沒什么差別。只要注意不要超過 16MB 即可。
2.實現(xiàn)
(1)點擊“保存”按鈕,將項目中的 abc.png 這張圖片存儲到 Realm 數(shù)據(jù)庫中。
(2)點擊“讀取”按鈕,從庫中取出最新保存的那張圖片,并顯示在 imageview 中。
3.樣例代碼
import UIKit
import RealmSwift
class ViewController: UIViewController {
//用于顯示圖片
@IBOutlet weak var imageView: UIImageView!
//使用默認(rèn)的數(shù)據(jù)庫
let realm = try! Realm()
override func viewDidLoad() {
super.viewDidLoad()
}
//點擊保存
@IBAction func saveData(_ sender: Any) {
//獲取圖片并轉(zhuǎn)換為Data
let imageURL = Bundle.main.url(forResource: "0", withExtension: "png")!
let imageData = try! Data(contentsOf: imageURL)
//將Data數(shù)據(jù)放到實體對象中
let portrait = HeadPortrait()
portrait.data = imageData
//數(shù)據(jù)持久化操作(類型記錄也會自動添加的)
try! realm.write {
realm.add(portrait)
}
print("數(shù)據(jù)保存完畢!")
}
//點擊加載
@IBAction func loadData(_ sender: Any) {
//獲取所有頭像圖片(根據(jù)插入時間倒序排列)
let portraits = realm.objects(HeadPortrait.self).sorted(byKeyPath: "date", ascending: false)
//將最新一張圖片顯示出來
if portraits.count > 0 {
if let imgData = portraits[0].data {
self.imageView.image = UIImage(data: imgData)
}
}
print("數(shù)據(jù)讀取完畢!")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
//用戶頭像
class HeadPortrait:Object {
//圖片數(shù)據(jù)
dynamic var data:Data?
//創(chuàng)建時間
dynamic var date = Date()
}
7、使用Realm Browser查看數(shù)據(jù)庫
(1)默認(rèn)數(shù)據(jù)庫是應(yīng)用的 Documents 文件夾下的一個名為“default.realm”。
//打印出數(shù)據(jù)庫地址
print(realm.configuration.fileURL)
(2)使用 Realm Browser 工具可以很方便的對.realm數(shù)據(jù)庫進(jìn)行讀取和編輯(在 App Store 中搜索 Realm Browser 即可下載)。
可以看到,上面的幾個對象已經(jīng)成功的插入到數(shù)據(jù)庫中來。
8、從數(shù)據(jù)庫中讀取記錄并顯示到表格中來
(1)通過查詢操作,Realm 將會返回包含 Object 集合的 Results 實例。Results 的表現(xiàn)和 Array 十分相似,并且包含在 Results 中的對象能夠通過索引下標(biāo)進(jìn)行訪問。
(2)所有的查詢(包括查詢和屬性訪問)在 Realm 中都是延遲加載的,只有當(dāng)屬性被訪問時,才能夠讀取相應(yīng)的數(shù)據(jù)。
(3)查詢結(jié)果并不是數(shù)據(jù)的拷貝:修改查詢結(jié)果(在寫入事務(wù)中)會直接修改硬盤上的數(shù)據(jù)。
下面我們把庫里的數(shù)據(jù)加載出來,并通過表格顯示出來。
效果圖如下:
代碼如下:
import UIKit
import RealmSwift
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var tableView: UITableView!
var dformatter = DateFormatter()
//保存從數(shù)據(jù)庫中查詢出來的結(jié)果集
var consumeItems:Results<ConsumeItem>?
override func viewDidLoad() {
super.viewDidLoad()
self.dformatter.dateFormat = "MM月dd日 HH:mm"
self.tableView!.delegate = self
self.tableView!.dataSource = self
//創(chuàng)建一個重用的單元格
self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
//使用默認(rèn)的數(shù)據(jù)庫
let realm = try! Realm()
//查詢所有的消費記錄
consumeItems = realm.objects(ConsumeItem.self)
}
//在本例中,只有一個分區(qū)
func numberOfSections(in tableView: UITableView) -> Int {
return 1;
}
//返回表格行數(shù)(也就是返回控件數(shù))
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.consumeItems!.count
}
//創(chuàng)建各單元顯示內(nèi)容(創(chuàng)建參數(shù)indexPath指定的單元)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
//同一形式的單元格重復(fù)使用,在聲明時已注冊
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條數(shù)據(jù)Realm無法直接限制查詢數(shù)量。所以我們?nèi)绻胍槌霾糠謹(jǐn)?shù)據(jù)(比如前5條記錄),也是全部查出來后在結(jié)果集中撈取。
//查詢并取出前5條數(shù)據(jù)
let dogs = try! Realm().objects(Dog.self)
for i in 0..<5 {
let dog = dogs[i]
// ...
}
Realm為何無法限制查詢數(shù)量?通常查詢數(shù)據(jù)庫數(shù)據(jù)時,我們可以在sql語句中添加一些限制語句(比如rownum,limit,top等)來限制返回的結(jié)果集的行數(shù)。但我們使用Realm會發(fā)現(xiàn),它沒有這種分頁功能,感覺不管查什么都是把所有的結(jié)果都撈出來。比如我們只要User表的前10條數(shù)據(jù),那么做法是先查詢出所有的User數(shù)據(jù),再從結(jié)果集中取出前10條數(shù)據(jù)。有人可能會擔(dān)心,如果數(shù)據(jù)庫中數(shù)據(jù)非常多,那每次都這么查不會影響性能嗎?其實大可放心,由于Realm都是延遲加載的,只有當(dāng)屬性被訪問時,才能夠讀取相應(yīng)的數(shù)據(jù)。不像通常數(shù)據(jù)庫,查詢后,查詢結(jié)果是從數(shù)據(jù)庫拷貝一份出來放在內(nèi)存中的。而Realm的查詢結(jié)果應(yīng)該說是數(shù)據(jù)庫數(shù)據(jù)的引用,就算你查出來,如果不用也不會占用什么內(nèi)存。
10、支持?jǐn)嘌圆樵?Predicate),這樣可以通過條件查詢特定數(shù)據(jù)同時可以使用鏈?zhǔn)讲樵償?shù)據(jù)。
//查詢花費超過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)
//使用鏈?zhǔn)讲樵?consumeItems = realm.objects(ConsumeItem.self).filter("cost > 10").filter("type.name = '購物'")
支持的斷言:比較操作數(shù)(comparison operand)可以是屬性名稱或者某個常量,但至少有一個操作數(shù)必須是屬性名稱;
比較操作符 ==、<=、<、>=、>、!=, 以及 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 支持以下復(fù)合操作符:“AND”、“OR” 以及 “NOT”。比如說 name BEGINSWITH ‘J’ AND age >= 32;
包含操作符 IN,比如說 name IN {‘Lisa’, ‘Spike’, ‘Hachi’};
==、!=支持與 nil 比較,比如說 Results<Company>().filter("ceo == nil")。注意到這只適用于有關(guān)系的對象,這里 ceo 是 Company 模型的一個屬性。
ANY 比較,比如說 ANY student.age < 21
注意,雖然我們不支持復(fù)合表達(dá)式類型(aggregate expression type),但是我們支持對對象的值使用 BETWEEN 操作符類型。比如說,Results<Person>.filter("age BETWEEN %@", [42, 43]])。
11、查詢結(jié)果的排序
//查詢花費超過10元的消費記錄,并按升序排列
consumeItems = realm.objects(ConsumeItem.self).filter("cost > 10").sorted(byProperty: "cost")
12、使用List實現(xiàn)一對多關(guān)系
List 中可以包含簡單類型的 Object,表面上和可變的 Array 非常類似。注意:List 只能夠包含 Object 類型,不能包含諸如String之類的基礎(chǔ)類型。 如果打算給我們的 Person 數(shù)據(jù)模型添加一個“dogs”屬性,以便能夠和多個“dogs”建立關(guān)系,也就是表明一個 Person 可以有多個 Dog,那么我們可以聲明一個List類型的屬性:
class Person: Object {
... // 其余的屬性聲明
let dogs = List<Dog>()
}
// 這里我們就可以使用已存在的狗狗對象來完成初始化
let aPerson = Person(value: ["李四", 30, [aDog, anotherDog]])
// 還可以使用多重嵌套
let aPerson = Person(value: ["李四", 30, [["小黑", 5], ["旺財", 6]]])
可以和之前一樣,對 List 屬性進(jìn)行訪問和賦值:
let someDogs = realm.objects(Dog.self).filter("name contains '小白'")
ZhangSan.dogs.append(objectsIn: someDogs)
ZhangSan.dogs.append(dahuang)
反向關(guān)系(Inverse Relationship)通過反向關(guān)系(也被稱為反向鏈接(backlink)),您可以通過一個特定的屬性獲取和給定對象有關(guān)系的所有對象。 Realm 提供了“鏈接對象 (linking objects)” 屬性來表示這些反向關(guān)系。借助鏈接對象屬性,您可以通過指定的屬性來獲取所有鏈接到指定對象的對象。
例如,一個 Dog 對象可以擁有一個名為 owners 的鏈接對象屬性,這個屬性中包含了某些 Person 對象,而這些 Person 對象在其 dogs 屬性中包含了這一個確定的 Dog 對象。您可以將 owners 屬性設(shè)置為 LinkingObjects 類型,然后指定其關(guān)系,說明其當(dāng)中包含了 Person 對象。
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
// Realm 并不會存儲這個屬性,因為這個屬性只定義了 getter
// 定義“owners”,和 Person.dogs 建立反向關(guān)系
let owners = LinkingObjects(fromType: Person.self, property: "dogs")
}
13、添加主鍵(Primary Keys) 重寫 Object.primaryKey() 可以設(shè)置模型的主鍵。聲明主鍵之后,對象將被允許查詢,更新速度更加高效,并且要求每個對象保持唯一性。一旦帶有主鍵的對象被添加到 Realm 之后,該對象的主鍵將不可修改。
class Person: Object {
dynamic var id = 0
dynamic var name = ""
override static func primaryKey() -> String? {
return "id"
}
}
14、添加索引屬性(Indexed Properties)重寫 Object.indexedProperties() 方法可以為數(shù)據(jù)模型中需要添加索引的屬性建立索引:
class Book: Object {
dynamic var price = 0
dynamic var title = ""
override static func indexedProperties() -> [String] {
return ["title"]
}
}
15、設(shè)置忽略屬性(Ignored Properties)重寫 Object.ignoredProperties() 可以防止 Realm 存儲數(shù)據(jù)模型的某個屬性。Realm 將不會干涉這些屬性的常規(guī)操作,它們將由成員變量(var)提供支持,并且您能夠輕易重寫它們的 setter 和 getter。
class Person: Object {
dynamic var tmpID = 0
var name: String { // 計算屬性將被自動忽略
return "\(firstName) \(lastName)"
}
dynamic var firstName = ""
dynamic var lastName = ""
override static func ignoredProperties() -> [String] {
return ["tmpID"]
}
}
16、修改更新數(shù)據(jù)
(1)直接更新內(nèi)容
// 在一個事務(wù)中更新對象
try! realm.write {
consumeItem.name = "去北京旅行"
}
(2)通過主鍵更新如果您的數(shù)據(jù)模型中設(shè)置了主鍵的話,那么您可以使用 Realm().add(_:update:) 來更新對象(當(dāng)對象不存在時也會自動插入新的對象。)
/****** 方式1 ***/
// 創(chuàng)建一個帶有主鍵的“書籍”對象,作為事先存儲的書籍
let cheeseBook = Book()
cheeseBook.title = "奶酪食譜"
cheeseBook.price = 9000
cheeseBook.id = 1
// 通過 id = 1 更新該書籍
try! realm.write {
realm.add(cheeseBook, update: true)
}
/****** 方式2 ***/
// 假設(shè)帶有主鍵值 `1` 的“書籍”對象已經(jīng)存在
try! realm.write {
realm.create(Book.self, value: ["id": 1, "price": 22], update: true)
// 這本書的`title`屬性不會被改變
}
(3)鍵值編碼
這個是在運行時才能決定哪個屬性需要更新的時候,這個對于大量更新的對象極為有用。
let persons = realm.objects(Person.self)
try! realm.write {
// 更新第一個
persons.first?.setValue(true, forKeyPath: "isFirst")
// 將每個人的 planet 屬性設(shè)置為“地球”
persons.setValue("地球", forKeyPath: "planet")
}
17、刪除數(shù)據(jù)
let cheeseBook = ... // 存儲在 Realm 中的 Book 對象
// 在事務(wù)中刪除一個對象
try! realm.write {
realm.delete(cheeseBook)
}
也能夠刪除數(shù)據(jù)庫中的所有數(shù)據(jù)
// 從 Realm 中刪除所有數(shù)據(jù)
try! realm.write {
realm.deleteAll()
}
18、Realm數(shù)據(jù)庫配置
(1)修改默認(rèn)的的數(shù)據(jù)庫
通過調(diào)用 Realm() 來初始化以及訪問我們的 realm 變量。其指向的是應(yīng)用的 Documents 文件夾下的一個名為“default.realm”的文件。
通過對默認(rèn)配置進(jìn)行更改,我們可以使用不同的數(shù)據(jù)庫。比如給每個用戶帳號創(chuàng)建一個特有的 Realm 文件,通過切換配置,就可以直接使用默認(rèn)的 Realm 數(shù)據(jù)庫來直接訪問各自數(shù)據(jù)庫:
func setDefaultRealmForUser(username: String) {
var config = Realm.Configuration()
// 使用默認(rèn)的目錄,但是使用用戶名來替換默認(rèn)的文件名
config.fileURL = config.fileURL!.deletingLastPathComponent()
.appendingPathComponent("\(username).realm")
// 將這個配置應(yīng)用到默認(rèn)的 Realm 數(shù)據(jù)庫當(dāng)中
Realm.Configuration.defaultConfiguration = config
}
(2)打包進(jìn)項目里的數(shù)據(jù)庫的使用如果需要將應(yīng)用的某些數(shù)據(jù)(比如配置信息,初始化信息等)打包到一個 Realm 文件中,作為主要 Realm 數(shù)據(jù)庫的擴展,操作如下:
let config = Realm.Configuration(
// 獲取需要打包文件的 URL 路徑
fileURL: Bundle.main.url(forResource: "MyBundledData", withExtension: "realm"),
// 以只讀模式打開文件,因為應(yīng)用數(shù)據(jù)包并不可寫
readOnly: true)
// 通過配置打開 Realm 數(shù)據(jù)庫
let realm = try! Realm(configuration: config)
// 通過配置打開 Realm 數(shù)據(jù)庫
let results = realm.objects(Dog.self).filter("age > 5")
(3)內(nèi)存數(shù)據(jù)庫內(nèi)存數(shù)據(jù)庫在每次程序運行期間都不會保存數(shù)據(jù)。但是,這不會妨礙到 Realm 的其他功能,包括查詢、關(guān)系以及線程安全。 假如您需要靈活的數(shù)據(jù)讀寫但又不想儲存數(shù)據(jù)的話,那么內(nèi)存數(shù)據(jù)庫對您來說一定是一個不錯的選擇。
let realm = try! Realm(configuration: Realm.Configuration(inMemoryIdentifier: "MyInMemoryRealm"))
19、加密數(shù)據(jù)庫
(1)加密后的 Realm文件不能跨平臺使用(因為 NSFileProtection 只有 iOS 才可以使用)
(2)Realm 文件不能在沒有密碼保護(hù)的 iOS 設(shè)備中進(jìn)行加密。為了避免這些問題(或者您想構(gòu)建一個 OS X 的應(yīng)用),可以使用 Realm 提供的加密方法。
(3)加密過的 Realm 只會帶來很少的額外資源占用(通常最多只會比平常慢10%)。
/***** 在創(chuàng)建 Realm 數(shù)據(jù)庫時采用64位的密鑰對數(shù)據(jù)庫文件進(jìn)行 AES-256+SHA2 加密 ****/
// 產(chǎn)生隨機密鑰
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` 會提示數(shù)據(jù)庫不可訪問
fatalError("Error opening realm: \(error)")
}
// 和往常一樣使用 Realm 即可
let dogs = realm.objects(Book.self).filter("name contains 'Fido'")
20、數(shù)據(jù)遷移(Migration)
(1)為何要遷移比如原來有如下 Person 模型:
class Person: Object {
dynamic var firstName = ""
dynamic var lastName = ""
dynamic var age = 0
}
假如我們想要更新數(shù)據(jù)模型,給它添加一個 fullname 屬性, 而不是將“姓”和“名”分離開來。
class Person: Object {
dynamic var fullName = ""
dynamic var age = 0
}
在這個時候如果您在數(shù)據(jù)模型更新之前就已經(jīng)保存了數(shù)據(jù)的話,那么 Realm 就會注意到代碼和硬盤上數(shù)據(jù)不匹配。 每當(dāng)這時,您必須進(jìn)行數(shù)據(jù)遷移,否則當(dāng)您試圖打開這個文件的話 Realm 就會拋出錯誤。 (2)如何進(jìn)行數(shù)據(jù)遷移假設(shè)我們想要把上面所聲明 Person 數(shù)據(jù)模型進(jìn)行遷移。如下所示是最簡單的數(shù)據(jù)遷移的必需流程:
// 在(application:didFinishLaunchingWithOptions:)中進(jìn)行配置
let config = Realm.Configuration(
// 設(shè)置新的架構(gòu)版本。這個版本號必須高于之前所用的版本號
// (如果您之前從未設(shè)置過架構(gòu)版本,那么這個版本號設(shè)置為 0)
schemaVersion: 1,
// 設(shè)置閉包,這個閉包將會在打開低于上面所設(shè)置版本號的 Realm 數(shù)據(jù)庫的時候被自動調(diào)用
migrationBlock: { migration, oldSchemaVersion in
// 目前我們還未進(jìn)行數(shù)據(jù)遷移,因此 oldSchemaVersion == 0
if (oldSchemaVersion < 1) {
// 什么都不要做!Realm 會自行檢測新增和需要移除的屬性,然后自動更新硬盤上的數(shù)據(jù)庫架構(gòu)
}
})
// 告訴 Realm 為默認(rèn)的 Realm 數(shù)據(jù)庫使用這個新的配置對象
Realm.Configuration.defaultConfiguration = config
// 現(xiàn)在我們已經(jīng)告訴了 Realm 如何處理架構(gòu)的變化,打開文件之后將會自動執(zhí)行遷移
let realm = try! Realm()
雖然這個遷移操作是最精簡的了,但是我們需要讓這個閉包能夠自行計算新的屬性(這里指的是 fullName),這樣才有意義。 在遷移閉包中,我們能夠調(diào)用Migration().enumerateObjects(::) 來枚舉特定類型的每個 Object 對象,然后執(zhí)行必要的遷移邏輯。注意,對枚舉中每個已存在的 Object 實例來說,應(yīng)該是通過訪問 oldObject 對象進(jìn)行訪問,而更新之后的實例應(yīng)該通過 newObject 進(jìn)行訪問:
// 在 application(application:didFinishLaunchingWithOptions:) 中進(jìn)行配置
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
// 將名字進(jìn)行合并,存放在 fullName 域中
let firstName = oldObject!["firstName"] as! String
let lastName = oldObject!["lastName"] as! String
newObject!["fullName"] = "\(firstName) \(lastName)"
}
}
})
21、使用帶有 REST API 功能的 Realm 數(shù)據(jù)庫示例我們將從 豆瓣FM的API 那里獲取一組 JSON 格式的頻道數(shù)據(jù),然后將它以 Realm Objects 的形式儲存到默認(rèn)的 Realm 數(shù)據(jù)庫里。
(1)json數(shù)據(jù)格式如下:
{
"channels" : [
{
"channel_id" : 0,
"abbr_en" : "My",
"name_en" : "Personal Radio",
"seq_id" : 0,
"name" : "私人兆赫"
},
{
"channel_id" : "1",
"abbr_en" : "",
"seq_id" : 0,
"name" : "華語",
"name_en" : ""
},
{
"channel_id" : "2",
"abbr_en" : "",
"seq_id" : 1,
"name" : "歐美",
"name_en" : ""
} ]
}
(2)我們將直接把 Dictionary 插入到 Realm 中,然后讓 Realm 自行快速地將其映射到 Object 上。(從 iOS9 起,新特性要求App訪問網(wǎng)絡(luò)請求,要采用 HTTPS 協(xié)議。直接請求HTTP數(shù)據(jù)會報錯,解決辦法可以參照的我另一篇文章:Swift - 網(wǎng)絡(luò)請求報App Transport Security has blocked a cleartext錯)為了確保示例能夠成功,我們需要一個所有屬性完全匹配 JSON 鍵結(jié)構(gòu)的 Object 結(jié)構(gòu)體。如果 JSON 的鍵結(jié)構(gòu)不匹配 Object 結(jié)構(gòu)體屬性結(jié)構(gòu)的話,那么就會在插入時被忽略。
fileprivate func setJsonToRealm (){
// 調(diào)用API
let url = URL(string: "https://www.douban.com/j/app/radio/channels")!
let resopne = try! Data(contentsOf: url)
let json = try! JSONSerialization.jsonObject(with: resopne, options: .allowFragments) as! [String: Any]
let channels = json["channels"] as! [[String: Any]]
try! realm.write {
for channel in channels {
if channel["seq_id"] as! Int == 0 {continue}//第一個頻道數(shù)據(jù)有問題,丟棄三
realm.create(DoubanChannel.self, value: channel, update: true)
}
}
}
(3)可以看到數(shù)據(jù)已經(jīng)成功插入到庫中了
22、當(dāng)前版本的限制Realm 致力于平衡數(shù)據(jù)庫讀取的靈活性和性能。為了實現(xiàn)這個目標(biāo),在 Realm 中所存儲的信息的各個方面都有基本的限制。例如:
(1)類名稱的長度最大只能存儲 57 個 UTF8 字符。
(2)屬性名稱的長度最大只能支持 63 個 UTF8 字符。
(3)NSData 以及 String 屬性不能保存超過 16 MB 大小的數(shù)據(jù)。如果要存儲大量的數(shù)據(jù),可通過將其分解為16MB 大小的塊,或者直接存儲在文件系統(tǒng)中,然后將文件路徑存儲在 Realm 中。如果您的應(yīng)用試圖存儲一個大于 16MB 的單一屬性,系統(tǒng)將在運行時拋出異常。
(4)對字符串進(jìn)行排序以及不區(qū)分大小寫查詢只支持“基礎(chǔ)拉丁字符集”、“拉丁字符補充集”、“拉丁文擴展字符集 A” 以及”拉丁文擴展字符集 B“(UTF-8 的范圍在 0~591 之間)。



