[Realm-Swift] 數(shù)據(jù)庫(kù)的使用詳解

swift開(kāi)發(fā)需要應(yīng)用數(shù)據(jù)庫(kù)椒功,fmdb筆記繁瑣心墅,在swift中準(zhǔn)備放棄使用,看到了 Realm這個(gè)三方框架很強(qiáng)大持灰,而且使用簡(jiǎn)單盔夜,就拿過(guò)來(lái)研究一下,感謝Swift - Realm數(shù)據(jù)庫(kù)的使用詳解(附樣例) 這篇文章堤魁,結(jié)合Realm官網(wǎng)知識(shí)Realm官網(wǎng)知識(shí)及GitHub的demoRealmCooa,可以熟練使用喂链,以此做個(gè)記錄,以防下次遺忘

1妥泉、什么是RealmRealm

于2014 年7月發(fā)布椭微,是一個(gè)跨平臺(tái)的移動(dòng)數(shù)據(jù)庫(kù)引擎,專門(mén)為移動(dòng)應(yīng)用的數(shù)據(jù)持久化而生盲链。其目的是要取代 Core Data 和 SQLite蝇率。

2迟杂、關(guān)于Realm,你要知道下面幾點(diǎn):

(1)使用簡(jiǎn)單本慕,大部分常用的功能(比如插入排拷、查詢等)都可以用一行簡(jiǎn)單的代碼輕松完成,學(xué)習(xí)成本低间狂。
(2)Realm 不是基于 Core Data攻泼,也不是基于 SQLite 封裝構(gòu)建的。它有自己的數(shù)據(jù)庫(kù)存儲(chǔ)引擎鉴象。
(3)Realm 具有良好的跨平臺(tái)特性忙菠,可以在 iOS 和 Android 平臺(tái)上共同使用。代碼可以使用 Swift 纺弊、 Objective-C 以及 Java 語(yǔ)言來(lái)編寫(xiě)牛欢。
(4)Realm 還提供了一個(gè)輕量級(jí)的數(shù)據(jù)庫(kù)查看工具(Realm Browser)。你也可以用它進(jìn)行一些簡(jiǎn)單的編輯操作(比如插入和刪除操作)

3淆游、支持的類型

(1)Realm 支持以下的屬性類型:Bool傍睹、Int8、Int16犹菱、Int32拾稳、Int64、Double腊脱、Float访得、String、Date(精度到秒)以及Data.
(2)也可以使用 List<object> 和 Object 來(lái)建立諸如一對(duì)多陕凹、一對(duì)一之類的關(guān)系模型悍抑,此外 Object 的子類也支持此功能。

4杜耙、Realm的安裝配置

(1)先去 Realm 的官網(wǎng)去下載最新框架:http://static.realm.io/downloads/swift/latest
(2)拖拽 RealmSwift.framework 和 Realm.framework 文件到”Embedded Binaries”選項(xiàng)中搜骡。選中 Copy items if needed 并點(diǎn)擊 Finish

Realm-Swift數(shù)據(jù)庫(kù)的使用詳解

5、將數(shù)據(jù)插入到數(shù)據(jù)庫(kù)中

下面代碼判斷默認(rèn)數(shù)據(jù)庫(kù)中是否有數(shù)據(jù)佑女,如果沒(méi)有的話將幾個(gè)自定義對(duì)像插入到數(shù)據(jù)庫(kù)中记靡。
(1)這里以個(gè)人消費(fèi)記錄為例,我們先定義消費(fèi)類別類团驱,和具體消費(fèi)記錄類

import Foundation
import RealmSwift
 
//消費(fèi)類型
class ConsumeType:Object {
    //類型名
    dynamic var name = ""
}
 
//消費(fèi)條目
class ConsumeItem:Object {
    //條目名
    dynamic var name = ""
    //金額
    dynamic var cost = 0.00
    //時(shí)間
    dynamic var date = Date()
    //所屬消費(fèi)類別
    dynamic var type:ConsumeType?
}

(2)判斷數(shù)據(jù)庫(kù)記錄是否為空簸呈,空的話則插入數(shù)據(jù)庫(kù)(這里以默認(rèn)數(shù)據(jù)庫(kù)為例)

import UIKit
import RealmSwift
 
class ViewController: UIViewController {
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        //使用默認(rèn)的數(shù)據(jù)庫(kù)
        let realm = try! Realm()
        //查詢所有的消費(fèi)記錄
        let items = realm.objects(ConsumeItem.self)
        //已經(jīng)有記錄的話就不插入了
        if items.count>0 {
            return
        }
         
        //創(chuàng)建兩個(gè)消費(fèi)類型
        let type1 = ConsumeType()
        type1.name = "購(gòu)物"
        let type2 = ConsumeType()
        type2.name = "娛樂(lè)"
         
        //創(chuàng)建三個(gè)消費(fèi)記錄
        let item1 = ConsumeItem(value: ["買(mǎi)一臺(tái)電腦",5999.00,Date(),type1]) //可使用數(shù)組創(chuàng)建
         
        let item2 = ConsumeItem()
        item2.name = "看一場(chǎng)電影"
        item2.cost = 30.00
        item2.date = Date(timeIntervalSinceNow: -36000)
        item2.type = type2
         
        let item3 = ConsumeItem()
        item3.name = "買(mǎi)一包泡面"
        item3.cost = 2.50
        item3.date = Date(timeIntervalSinceNow: -72000)
        item3.type = type1
         
        // 數(shù)據(jù)持久化操作(類型記錄也會(huì)自動(dòng)添加的)
        try! realm.write {
            realm.add(item1)
            realm.add(item2)
            realm.add(item3)
        }
         
        //打印出數(shù)據(jù)庫(kù)地址
        print(realm.configuration.fileURL)
    }
     
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

6、Data類型數(shù)據(jù)的存取

1.實(shí)現(xiàn)原理
(1)Realm 支持 Data 類型的屬性店茶,我們要做的就是將圖片轉(zhuǎn)換為 Data 類型蜕便,再進(jìn)行存儲(chǔ)即可。
(2)Data 類型的屬性讀取操作同其他數(shù)據(jù)類型的讀取沒(méi)什么差別贩幻。只要注意不要超過(guò) 16MB 即可轿腺。
2.實(shí)現(xiàn)
(1)點(diǎn)擊“保存”按鈕两嘴,將項(xiàng)目中的 abc.png 這張圖片存儲(chǔ)到 Realm 數(shù)據(jù)庫(kù)中。
(2)點(diǎn)擊“讀取”按鈕族壳,從庫(kù)中取出最新保存的那張圖片憔辫,并顯示在 imageview 中。
3.樣例代碼

import UIKit
import RealmSwift
 
class ViewController: UIViewController {
    //用于顯示圖片
    @IBOutlet weak var imageView: UIImageView!
     
    //使用默認(rèn)的數(shù)據(jù)庫(kù)
    let realm = try! Realm()
     
    override func viewDidLoad() {
        super.viewDidLoad()
    }
     
    //點(diǎn)擊保存
    @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ù)放到實(shí)體對(duì)象中
        let portrait = HeadPortrait()
        portrait.data = imageData
        //數(shù)據(jù)持久化操作(類型記錄也會(huì)自動(dòng)添加的)
        try! realm.write {
            realm.add(portrait)
        }
        print("數(shù)據(jù)保存完畢!")
    }
     
    //點(diǎn)擊加載
    @IBAction func loadData(_ sender: Any) {
        //獲取所有頭像圖片(根據(jù)插入時(shí)間倒序排列)
        let portraits = realm.objects(HeadPortrait.self).sorted(byKeyPath: "date", ascending: false)
        //將最新一張圖片顯示出來(lái)
        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)建時(shí)間
    dynamic var date = Date()
}

7仿荆、使用Realm Browser查看數(shù)據(jù)庫(kù)

(1)默認(rèn)數(shù)據(jù)庫(kù)是應(yīng)用的 Documents 文件夾下的一個(gè)名為“default.realm”贰您。

//打印出數(shù)據(jù)庫(kù)地址

print(realm.configuration.fileURL)

(2)使用 Realm Browser 工具可以很方便的對(duì).realm數(shù)據(jù)庫(kù)進(jìn)行讀取和編輯(在 App Store 中搜索 Realm Browser 即可下載)。
可以看到拢操,上面的幾個(gè)對(duì)象已經(jīng)成功的插入到數(shù)據(jù)庫(kù)中來(lái)锦亦。

8、從數(shù)據(jù)庫(kù)中讀取記錄并顯示到表格中來(lái)

(1)通過(guò)查詢操作令境,Realm 將會(huì)返回包含 Object 集合的 Results 實(shí)例杠园。Results 的表現(xiàn)和 Array 十分相似,并且包含在 Results 中的對(duì)象能夠通過(guò)索引下標(biāo)進(jìn)行訪問(wèn)舔庶。
(2)所有的查詢(包括查詢和屬性訪問(wèn))在 Realm 中都是延遲加載的抛蚁,只有當(dāng)屬性被訪問(wèn)時(shí),才能夠讀取相應(yīng)的數(shù)據(jù)惕橙。
(3)查詢結(jié)果并不是數(shù)據(jù)的拷貝:修改查詢結(jié)果(在寫(xiě)入事務(wù)中)會(huì)直接修改硬盤(pán)上的數(shù)據(jù)瞧甩。
下面我們把庫(kù)里的數(shù)據(jù)加載出來(lái),并通過(guò)表格顯示出來(lái)弥鹦。
效果圖如下:

代碼如下:

import UIKit
import RealmSwift
 
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource  {
     
    @IBOutlet weak var tableView: UITableView!
     
    var dformatter = DateFormatter()
     
    //保存從數(shù)據(jù)庫(kù)中查詢出來(lái)的結(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)建一個(gè)重用的單元格
        self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
         
        //使用默認(rèn)的數(shù)據(jù)庫(kù)
        let realm = try! Realm()
        //查詢所有的消費(fèi)記錄
        consumeItems = realm.objects(ConsumeItem.self)
    }
     
    //在本例中肚逸,只有一個(gè)分區(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ù)使用,在聲明時(shí)已注冊(cè)
        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無(wú)法直接限制查詢數(shù)量吼虎。所以我們?nèi)绻胍槌霾糠謹(jǐn)?shù)據(jù)(比如前5條記錄)犬钢,也是全部查出來(lái)后在結(jié)果集中撈取苍鲜。

//查詢并取出前5條數(shù)據(jù)
let dogs = try! Realm().objects(Dog.self)
for i in 0..<5 {
    let dog = dogs[i]
    // ...
}

Realm為何無(wú)法限制查詢數(shù)量?通常查詢數(shù)據(jù)庫(kù)數(shù)據(jù)時(shí)玷犹,我們可以在sql語(yǔ)句中添加一些限制語(yǔ)句(比如rownum混滔,limit,top等)來(lái)限制返回的結(jié)果集的行數(shù)歹颓。但我們使用Realm會(huì)發(fā)現(xiàn)坯屿,它沒(méi)有這種分頁(yè)功能,感覺(jué)不管查什么都是把所有的結(jié)果都撈出來(lái)巍扛。比如我們只要User表的前10條數(shù)據(jù)领跛,那么做法是先查詢出所有的User數(shù)據(jù),再?gòu)慕Y(jié)果集中取出前10條數(shù)據(jù)撤奸。有人可能會(huì)擔(dān)心吠昭,如果數(shù)據(jù)庫(kù)中數(shù)據(jù)非常多喊括,那每次都這么查不會(huì)影響性能嗎?其實(shí)大可放心矢棚,由于Realm都是延遲加載的郑什,只有當(dāng)屬性被訪問(wèn)時(shí),才能夠讀取相應(yīng)的數(shù)據(jù)蒲肋。不像通常數(shù)據(jù)庫(kù)蘑拯,查詢后,查詢結(jié)果是從數(shù)據(jù)庫(kù)拷貝一份出來(lái)放在內(nèi)存中的兜粘。而Realm的查詢結(jié)果應(yīng)該說(shuō)是數(shù)據(jù)庫(kù)數(shù)據(jù)的引用申窘,就算你查出來(lái),如果不用也不會(huì)占用什么內(nèi)存妹沙。

10偶洋、支持?jǐn)嘌圆樵?Predicate),這樣可以通過(guò)條件查詢特定數(shù)據(jù)同時(shí)可以使用鏈?zhǔn)讲樵償?shù)據(jù)距糖。

//查詢花費(fèi)超過(guò)10元的消費(fèi)記錄(使用斷言字符串查詢)
consumeItems = realm.objects(ConsumeItem.self).filter("cost > 10")
 
//查詢花費(fèi)超過(guò)10元的購(gòu)物記錄(使用 NSPredicate 查詢)
let predicate = NSPredicate(format: "type.name = '購(gòu)物' AND cost > 10")
consumeItems = realm.objects(ConsumeItem.self).filter(predicate)
 
//使用鏈?zhǔn)讲樵?consumeItems = realm.objects(ConsumeItem.self).filter("cost > 10").filter("type.name = '購(gòu)物'")

支持的斷言:比較操作數(shù)(comparison operand)可以是屬性名稱或者某個(gè)常量玄窝,但至少有一個(gè)操作數(shù)必須是屬性名稱;
比較操作符 ==悍引、<=恩脂、<、>=趣斤、>俩块、!=, 以及 BETWEEN 支持 int、long浓领、long long玉凯、float、double 以及 NSDate 屬性類型的比較联贩,比如說(shuō) age == 45漫仆;
相等比較 ==以及!=,比如說(shuō)Results<Employee>().filter("company == %@", company)
比較操作符 == and != 支持布爾屬性泪幌;
對(duì)于 NSString 和 NSData 屬性來(lái)說(shuō)盲厌,我們支持 ==、!=祸泪、BEGINSWITH吗浩、CONTAINS 以及 ENDSWITH 操作符,比如說(shuō) name CONTAINS ‘Ja’没隘;
字符串支持忽略大小寫(xiě)的比較方式懂扼,比如說(shuō) name CONTAINS[c] ‘Ja’ ,注意到其中字符的大小寫(xiě)將被忽略右蒲;
Realm 支持以下復(fù)合操作符:“AND”阀湿、“OR” 以及 “NOT”屡限。比如說(shuō) name BEGINSWITH ‘J’ AND age >= 32;
包含操作符 IN炕倘,比如說(shuō) name IN {‘Lisa’, ‘Spike’, ‘Hachi’}钧大;
==、!=支持與 nil 比較罩旋,比如說(shuō) Results<Company>().filter("ceo == nil")啊央。注意到這只適用于有關(guān)系的對(duì)象,這里 ceo 是 Company 模型的一個(gè)屬性涨醋。
ANY 比較瓜饥,比如說(shuō) ANY student.age < 21
注意,雖然我們不支持復(fù)合表達(dá)式類型(aggregate expression type)浴骂,但是我們支持對(duì)對(duì)象的值使用 BETWEEN 操作符類型乓土。比如說(shuō),Results<Person>.filter("age BETWEEN %@", [42, 43]])溯警。

11趣苏、查詢結(jié)果的排序

//查詢花費(fèi)超過(guò)10元的消費(fèi)記錄,并按升序排列
consumeItems = realm.objects(ConsumeItem.self).filter("cost > 10").sorted(byProperty: "cost")

12、使用List實(shí)現(xiàn)一對(duì)多關(guān)系

List 中可以包含簡(jiǎn)單類型的 Object梯轻,表面上和可變的 Array 非常類似食磕。注意:List 只能夠包含 Object 類型,不能包含諸如String之類的基礎(chǔ)類型喳挑。 如果打算給我們的 Person 數(shù)據(jù)模型添加一個(gè)“dogs”屬性彬伦,以便能夠和多個(gè)“dogs”建立關(guān)系,也就是表明一個(gè) Person 可以有多個(gè) Dog伊诵,那么我們可以聲明一個(gè)List類型的屬性:

class Person: Object {
    ... // 其余的屬性聲明
    let dogs = List<Dog>()
}
 
// 這里我們就可以使用已存在的狗狗對(duì)象來(lái)完成初始化
let aPerson = Person(value: ["李四", 30, [aDog, anotherDog]])
 
// 還可以使用多重嵌套
let aPerson = Person(value: ["李四", 30, [["小黑", 5], ["旺財(cái)", 6]]])

可以和之前一樣单绑,對(duì) List 屬性進(jìn)行訪問(wèn)和賦值:

let someDogs = realm.objects(Dog.self).filter("name contains '小白'")
ZhangSan.dogs.append(objectsIn: someDogs)
ZhangSan.dogs.append(dahuang)

反向關(guān)系(Inverse Relationship)通過(guò)反向關(guān)系(也被稱為反向鏈接(backlink)),您可以通過(guò)一個(gè)特定的屬性獲取和給定對(duì)象有關(guān)系的所有對(duì)象曹宴。 Realm 提供了“鏈接對(duì)象 (linking objects)” 屬性來(lái)表示這些反向關(guān)系搂橙。借助鏈接對(duì)象屬性,您可以通過(guò)指定的屬性來(lái)獲取所有鏈接到指定對(duì)象的對(duì)象浙炼。
例如份氧,一個(gè) Dog 對(duì)象可以擁有一個(gè)名為 owners 的鏈接對(duì)象屬性唯袄,這個(gè)屬性中包含了某些 Person 對(duì)象弯屈,而這些 Person 對(duì)象在其 dogs 屬性中包含了這一個(gè)確定的 Dog 對(duì)象。您可以將 owners 屬性設(shè)置為 LinkingObjects 類型恋拷,然后指定其關(guān)系资厉,說(shuō)明其當(dāng)中包含了 Person 對(duì)象。

class Dog: Object {
   dynamic var name = ""
   dynamic var age = 0
    
   // Realm 并不會(huì)存儲(chǔ)這個(gè)屬性蔬顾,因?yàn)檫@個(gè)屬性只定義了 getter
   // 定義“owners”宴偿,和 Person.dogs 建立反向關(guān)系
   let owners = LinkingObjects(fromType: Person.self, property: "dogs")
}

13湘捎、添加主鍵(Primary Keys) 重寫(xiě) Object.primaryKey() 可以設(shè)置模型的主鍵。聲明主鍵之后窄刘,對(duì)象將被允許查詢窥妇,更新速度更加高效,并且要求每個(gè)對(duì)象保持唯一性娩践。一旦帶有主鍵的對(duì)象被添加到 Realm 之后活翩,該對(duì)象的主鍵將不可修改。

class Person: Object {
  dynamic var id = 0
  dynamic var name = ""
 
  override static func primaryKey() -> String? {
    return "id"
  }
}

14翻伺、添加索引屬性(Indexed Properties)重寫(xiě) 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)重寫(xiě) Object.ignoredProperties() 可以防止 Realm 存儲(chǔ)數(shù)據(jù)模型的某個(gè)屬性。Realm 將不會(huì)干涉這些屬性的常規(guī)操作吨岭,它們將由成員變量(var)提供支持拉宗,并且您能夠輕易重寫(xiě)它們的 setter 和 getter。

class Person: Object {
  dynamic var tmpID = 0
  var name: String { // 計(jì)算屬性將被自動(dòng)忽略
    return "\(firstName) \(lastName)"
  }
  dynamic var firstName = ""
  dynamic var lastName = ""
 
  override static func ignoredProperties() -> [String] {
    return ["tmpID"]
  }
}

16辣辫、修改更新數(shù)據(jù)

(1)直接更新內(nèi)容

// 在一個(gè)事務(wù)中更新對(duì)象
try! realm.write {
  consumeItem.name = "去北京旅行"
}

(2)通過(guò)主鍵更新如果您的數(shù)據(jù)模型中設(shè)置了主鍵的話旦事,那么您可以使用 Realm().add(_:update:) 來(lái)更新對(duì)象(當(dāng)對(duì)象不存在時(shí)也會(huì)自動(dòng)插入新的對(duì)象。)

/****** 方式1 ***/
// 創(chuàng)建一個(gè)帶有主鍵的“書(shū)籍”對(duì)象急灭,作為事先存儲(chǔ)的書(shū)籍
let cheeseBook = Book()
cheeseBook.title = "奶酪食譜"
cheeseBook.price = 9000
cheeseBook.id = 1
 
// 通過(guò) id = 1 更新該書(shū)籍
try! realm.write {
    realm.add(cheeseBook, update: true)
}
 
/****** 方式2 ***/
// 假設(shè)帶有主鍵值 `1` 的“書(shū)籍”對(duì)象已經(jīng)存在
try! realm.write {
    realm.create(Book.self, value: ["id": 1, "price": 22], update: true)
    // 這本書(shū)的`title`屬性不會(huì)被改變
}

(3)鍵值編碼
這個(gè)是在運(yùn)行時(shí)才能決定哪個(gè)屬性需要更新的時(shí)候族檬,這個(gè)對(duì)于大量更新的對(duì)象極為有用。

let persons = realm.objects(Person.self)
try! realm.write {
    // 更新第一個(gè)
    persons.first?.setValue(true, forKeyPath: "isFirst")
    // 將每個(gè)人的 planet 屬性設(shè)置為“地球”
    persons.setValue("地球", forKeyPath: "planet")
}

17化戳、刪除數(shù)據(jù)

let cheeseBook = ... // 存儲(chǔ)在 Realm 中的 Book 對(duì)象
 
// 在事務(wù)中刪除一個(gè)對(duì)象
try! realm.write {
  realm.delete(cheeseBook)
}

也能夠刪除數(shù)據(jù)庫(kù)中的所有數(shù)據(jù)

// 從 Realm 中刪除所有數(shù)據(jù)
try! realm.write {
  realm.deleteAll()
}

18单料、Realm數(shù)據(jù)庫(kù)配置

(1)修改默認(rèn)的的數(shù)據(jù)庫(kù)
通過(guò)調(diào)用 Realm() 來(lái)初始化以及訪問(wèn)我們的 realm 變量。其指向的是應(yīng)用的 Documents 文件夾下的一個(gè)名為“default.realm”的文件点楼。
通過(guò)對(duì)默認(rèn)配置進(jìn)行更改扫尖,我們可以使用不同的數(shù)據(jù)庫(kù)。比如給每個(gè)用戶帳號(hào)創(chuàng)建一個(gè)特有的 Realm 文件掠廓,通過(guò)切換配置换怖,就可以直接使用默認(rèn)的 Realm 數(shù)據(jù)庫(kù)來(lái)直接訪問(wèn)各自數(shù)據(jù)庫(kù):

func setDefaultRealmForUser(username: String) {
   var config = Realm.Configuration()
    
   // 使用默認(rèn)的目錄,但是使用用戶名來(lái)替換默認(rèn)的文件名
   config.fileURL = config.fileURL!.deletingLastPathComponent()
       .appendingPathComponent("\(username).realm")
    
   // 將這個(gè)配置應(yīng)用到默認(rèn)的 Realm 數(shù)據(jù)庫(kù)當(dāng)中
   Realm.Configuration.defaultConfiguration = config
}

(2)打包進(jìn)項(xiàng)目里的數(shù)據(jù)庫(kù)的使用如果需要將應(yīng)用的某些數(shù)據(jù)(比如配置信息蟀瞧,初始化信息等)打包到一個(gè) Realm 文件中沉颂,作為主要 Realm 數(shù)據(jù)庫(kù)的擴(kuò)展,操作如下:

let config = Realm.Configuration(
    // 獲取需要打包文件的 URL 路徑
    fileURL: Bundle.main.url(forResource: "MyBundledData", withExtension: "realm"),
    // 以只讀模式打開(kāi)文件悦污,因?yàn)閼?yīng)用數(shù)據(jù)包并不可寫(xiě)
    readOnly: true)
 
// 通過(guò)配置打開(kāi) Realm 數(shù)據(jù)庫(kù)
let realm = try! Realm(configuration: config)
 
// 通過(guò)配置打開(kāi) Realm 數(shù)據(jù)庫(kù)
let results = realm.objects(Dog.self).filter("age > 5")

(3)內(nèi)存數(shù)據(jù)庫(kù)內(nèi)存數(shù)據(jù)庫(kù)在每次程序運(yùn)行期間都不會(huì)保存數(shù)據(jù)铸屉。但是,這不會(huì)妨礙到 Realm 的其他功能切端,包括查詢彻坛、關(guān)系以及線程安全。 假如您需要靈活的數(shù)據(jù)讀寫(xiě)但又不想儲(chǔ)存數(shù)據(jù)的話,那么內(nèi)存數(shù)據(jù)庫(kù)對(duì)您來(lái)說(shuō)一定是一個(gè)不錯(cuò)的選擇昌屉。

let realm = try! Realm(configuration: Realm.Configuration(inMemoryIdentifier: "MyInMemoryRealm"))

19钙蒙、加密數(shù)據(jù)庫(kù)

(1)加密后的 Realm文件不能跨平臺(tái)使用(因?yàn)?NSFileProtection 只有 iOS 才可以使用)
(2)Realm 文件不能在沒(méi)有密碼保護(hù)的 iOS 設(shè)備中進(jìn)行加密。為了避免這些問(wèn)題(或者您想構(gòu)建一個(gè) OS X 的應(yīng)用)间驮,可以使用 Realm 提供的加密方法躬厌。
(3)加密過(guò)的 Realm 只會(huì)帶來(lái)很少的額外資源占用(通常最多只會(huì)比平常慢10%)。

/*****   在創(chuàng)建 Realm 數(shù)據(jù)庫(kù)時(shí)采用64位的密鑰對(duì)數(shù)據(jù)庫(kù)文件進(jìn)行 AES-256+SHA2 加密   ****/
// 產(chǎn)生隨機(jī)密鑰
var key = Data(count: 64)
_ = key.withUnsafeMutableBytes { bytes in
    SecRandomCopyBytes(kSecRandomDefault, 64, bytes)
}
         
// 打開(kāi)加密文件
let config = Realm.Configuration(encryptionKey: key)
let realm:Realm
do {
    realm = try Realm(configuration: config)
} catch let error as NSError {
    // 如果密鑰錯(cuò)誤竞帽,`error` 會(huì)提示數(shù)據(jù)庫(kù)不可訪問(wèn)
    fatalError("Error opening realm: \(error)")
}
 
// 和往常一樣使用 Realm 即可
let dogs = realm.objects(Book.self).filter("name contains 'Fido'")

20烤咧、數(shù)據(jù)遷移(Migration)

(1)為何要遷移比如原來(lái)有如下 Person 模型:

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

假如我們想要更新數(shù)據(jù)模型,給它添加一個(gè) fullname 屬性抢呆, 而不是將“姓”和“名”分離開(kāi)來(lái)煮嫌。

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

在這個(gè)時(shí)候如果您在數(shù)據(jù)模型更新之前就已經(jīng)保存了數(shù)據(jù)的話,那么 Realm 就會(huì)注意到代碼和硬盤(pán)上數(shù)據(jù)不匹配抱虐。 每當(dāng)這時(shí)昌阿,您必須進(jìn)行數(shù)據(jù)遷移,否則當(dāng)您試圖打開(kāi)這個(gè)文件的話 Realm 就會(huì)拋出錯(cuò)誤恳邀。 (2)如何進(jìn)行數(shù)據(jù)遷移假設(shè)我們想要把上面所聲明 Person 數(shù)據(jù)模型進(jìn)行遷移懦冰。如下所示是最簡(jiǎn)單的數(shù)據(jù)遷移的必需流程:

// 在(application:didFinishLaunchingWithOptions:)中進(jìn)行配置
 
let config = Realm.Configuration(
  // 設(shè)置新的架構(gòu)版本。這個(gè)版本號(hào)必須高于之前所用的版本號(hào)
  // (如果您之前從未設(shè)置過(guò)架構(gòu)版本谣沸,那么這個(gè)版本號(hào)設(shè)置為 0)
  schemaVersion: 1,
 
  // 設(shè)置閉包刷钢,這個(gè)閉包將會(huì)在打開(kāi)低于上面所設(shè)置版本號(hào)的 Realm 數(shù)據(jù)庫(kù)的時(shí)候被自動(dòng)調(diào)用
  migrationBlock: { migration, oldSchemaVersion in
    // 目前我們還未進(jìn)行數(shù)據(jù)遷移,因此 oldSchemaVersion == 0
    if (oldSchemaVersion < 1) {
      // 什么都不要做乳附!Realm 會(huì)自行檢測(cè)新增和需要移除的屬性内地,然后自動(dòng)更新硬盤(pán)上的數(shù)據(jù)庫(kù)架構(gòu)
    }
  })
 
// 告訴 Realm 為默認(rèn)的 Realm 數(shù)據(jù)庫(kù)使用這個(gè)新的配置對(duì)象
Realm.Configuration.defaultConfiguration = config
 
// 現(xiàn)在我們已經(jīng)告訴了 Realm 如何處理架構(gòu)的變化,打開(kāi)文件之后將會(huì)自動(dòng)執(zhí)行遷移
let realm = try! Realm()

雖然這個(gè)遷移操作是最精簡(jiǎn)的了赋除,但是我們需要讓這個(gè)閉包能夠自行計(jì)算新的屬性(這里指的是 fullName)阱缓,這樣才有意義。 在遷移閉包中举农,我們能夠調(diào)用Migration().enumerateObjects(::) 來(lái)枚舉特定類型的每個(gè) Object 對(duì)象荆针,然后執(zhí)行必要的遷移邏輯。注意颁糟,對(duì)枚舉中每個(gè)已存在的 Object 實(shí)例來(lái)說(shuō)航背,應(yīng)該是通過(guò)訪問(wèn) oldObject 對(duì)象進(jìn)行訪問(wèn),而更新之后的實(shí)例應(yīng)該通過(guò) newObject 進(jìn)行訪問(wèn):

// 在 application(application:didFinishLaunchingWithOptions:) 中進(jìn)行配置
 
Realm.Configuration.defaultConfiguration = Realm.Configuration(
  schemaVersion: 1,
  migrationBlock: { migration, oldSchemaVersion in
    if (oldSchemaVersion < 1) {
      // enumerateObjects(ofType:_:) 方法遍歷了存儲(chǔ)在 Realm 文件中的每一個(gè)“Person”對(duì)象
      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ù)庫(kù)示例我們將從 豆瓣FM的API 那里獲取一組 JSON 格式的頻道數(shù)據(jù),然后將它以 Realm Objects 的形式儲(chǔ)存到默認(rèn)的 Realm 數(shù)據(jù)庫(kù)里键畴。

(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" : "華語(yǔ)",
      "name_en" : ""
    },
    {
      "channel_id" : "2",
      "abbr_en" : "",
      "seq_id" : 1,
      "name" : "歐美",
      "name_en" : ""
    }  ]
}

(2)我們將直接把 Dictionary 插入到 Realm 中最盅,然后讓 Realm 自行快速地將其映射到 Object 上。(從 iOS9 起起惕,新特性要求App訪問(wèn)網(wǎng)絡(luò)請(qǐng)求涡贱,要采用 HTTPS 協(xié)議。直接請(qǐng)求HTTP數(shù)據(jù)會(huì)報(bào)錯(cuò)惹想,解決辦法可以參照的我另一篇文章:Swift - 網(wǎng)絡(luò)請(qǐng)求報(bào)App Transport Security has blocked a cleartext錯(cuò))為了確保示例能夠成功问词,我們需要一個(gè)所有屬性完全匹配 JSON 鍵結(jié)構(gòu)的 Object 結(jié)構(gòu)體。如果 JSON 的鍵結(jié)構(gòu)不匹配 Object 結(jié)構(gòu)體屬性結(jié)構(gòu)的話嘀粱,那么就會(huì)在插入時(shí)被忽略激挪。

  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}//第一個(gè)頻道數(shù)據(jù)有問(wèn)題,丟棄三
                realm.create(DoubanChannel.self, value: channel, update: true)
                
            }
        }
        
        
    }

(3)可以看到數(shù)據(jù)已經(jīng)成功插入到庫(kù)中了

22、當(dāng)前版本的限制Realm 致力于平衡數(shù)據(jù)庫(kù)讀取的靈活性和性能锋叨。為了實(shí)現(xiàn)這個(gè)目標(biāo)垄分,在 Realm 中所存儲(chǔ)的信息的各個(gè)方面都有基本的限制。例如:

(1)類名稱的長(zhǎng)度最大只能存儲(chǔ) 57 個(gè) UTF8 字符娃磺。
(2)屬性名稱的長(zhǎng)度最大只能支持 63 個(gè) UTF8 字符薄湿。
(3)NSData 以及 String 屬性不能保存超過(guò) 16 MB 大小的數(shù)據(jù)。如果要存儲(chǔ)大量的數(shù)據(jù)偷卧,可通過(guò)將其分解為16MB 大小的塊豺瘤,或者直接存儲(chǔ)在文件系統(tǒng)中,然后將文件路徑存儲(chǔ)在 Realm 中听诸。如果您的應(yīng)用試圖存儲(chǔ)一個(gè)大于 16MB 的單一屬性坐求,系統(tǒng)將在運(yùn)行時(shí)拋出異常。
(4)對(duì)字符串進(jìn)行排序以及不區(qū)分大小寫(xiě)查詢只支持“基礎(chǔ)拉丁字符集”晌梨、“拉丁字符補(bǔ)充集”桥嗤、“拉丁文擴(kuò)展字符集 A” 以及”拉丁文擴(kuò)展字符集 B“(UTF-8 的范圍在 0~591 之間)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末仔蝌,一起剝皮案震驚了整個(gè)濱河市砸逊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掌逛,老刑警劉巖师逸,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異豆混,居然都是意外死亡篓像,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)皿伺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)员辩,“玉大人,你說(shuō)我怎么就攤上這事鸵鸥〉旎” “怎么了丹皱?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)宋税。 經(jīng)常有香客問(wèn)我摊崭,道長(zhǎng),這世上最難降的妖魔是什么杰赛? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任呢簸,我火速辦了婚禮,結(jié)果婚禮上乏屯,老公的妹妹穿的比我還像新娘根时。我一直安慰自己,他們只是感情好辰晕,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布蛤迎。 她就那樣靜靜地躺著,像睡著了一般含友。 火紅的嫁衣襯著肌膚如雪忘苛。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,021評(píng)論 1 291
  • 那天唱较,我揣著相機(jī)與錄音扎唾,去河邊找鬼。 笑死南缓,一個(gè)胖子當(dāng)著我的面吹牛胸遇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播汉形,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼纸镊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了概疆?” 一聲冷哼從身側(cè)響起逗威,我...
    開(kāi)封第一講書(shū)人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎岔冀,沒(méi)想到半個(gè)月后凯旭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡使套,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年罐呼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侦高。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嫉柴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出奉呛,到底是詐尸還是另有隱情计螺,我是刑警寧澤夯尽,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站登馒,受9級(jí)特大地震影響匙握,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜谊娇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一肺孤、第九天 我趴在偏房一處隱蔽的房頂上張望罗晕。 院中可真熱鬧济欢,春花似錦、人聲如沸小渊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)酬屉。三九已至半等,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間呐萨,已是汗流浹背杀饵。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谬擦,地道東北人切距。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像惨远,于是被迫代替她去往敵國(guó)和親谜悟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容