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

1,什么是Realm

Realm 于2014 年7月發(fā)布,是一個跨平臺的移動數(shù)據(jù)庫引擎,專門為移動應(yīng)用的數(shù)據(jù)持久化而生。其目的是要取代 Core Data 和 SQLite。

2,關(guān)于Realm,你要知道下面幾點:

(1)使用簡單,大部分常用的功能(比如插入、查詢等)都可以用一行簡單的代碼輕松完成,學習成本低。

(2)Realm 不是基于 Core Data,也不是基于 SQLite 封裝構(gòu)建的。它有自己的數(shù)據(jù)庫存儲引擎。

(3)Realm 具有良好的跨平臺特性,可以在 iOS 和 Android 平臺上共同使用。代碼可以使用 Swift 、 Objective-C 以及 Java 語言來編寫。

(4)Realm 還提供了一個輕量級的數(shù)據(jù)庫查看工具(Realm Browser)。你也可以用它進行一些簡單的編輯操作(比如插入和刪除操作) 

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ù)庫中

下面代碼判斷默認數(shù)據(jù)庫中是否有數(shù)據(jù),如果沒有的話將幾個自定義對像插入到數(shù)據(jù)庫中。
(1)這里以個人消費記錄為例,我們先定義消費類別類,和具體消費記錄類

import Foundation
import RealmSwift

//消費類型
class ConsumeType: Object {
      //類型名
     @objc dynamic var name = ""
}

//消費條目
class ConsumeItem: Object {
  //條目名
  @objc dynamic var name = ""

  //金額
  @objc dynamic var cost = 0.00

  //時間
  @objc dynamic var date = Date()

  //所屬消費類別
  @objc dynamic var type: ConsumeType?
}

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

import UIKit
import RealmSwift

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        //使用默認的數(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ù)的存取

參考另一篇文章:Swift - Realm數(shù)據(jù)庫中圖片的插入、讀取(Data類型數(shù)據(jù)的存儲)

7,使用Realm Browser查看數(shù)據(jù)庫

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

//打印出數(shù)據(jù)庫地址
print(realm.configuration.fileURL ?? "")

(2)使用 Realm Browser 工具可以很方便的對.realm數(shù)據(jù)庫進行讀取和編輯(在 App Store 中搜索 Realm Browser 即可下載)。

可以看到,上面的幾個對象已經(jīng)成功的插入到數(shù)據(jù)庫中來。

8,從數(shù)據(jù)庫中讀取記錄并顯示到表格中來

(1)通過查詢操作,Realm 將會返回包含 Object 集合的 Results 實例。Results 的表現(xiàn)和 Array 十分相似,并且包含在 Results 中的對象能夠通過索引下標進行訪問。
(2)所有的查詢(包括查詢和屬性訪問)在 Realm 中都是延遲加載的,只有當屬性被訪問時,才能夠讀取相應(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")

        //使用默認的數(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, cellForRowA tindexPath: 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)绻胍槌霾糠謹?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ù)。
有人可能會擔心,如果數(shù)據(jù)庫中數(shù)據(jù)非常多,那每次都這么查不會影響性能嗎?
其實大可放心,由于Realm都是延遲加載的,只有當屬性被訪問時,才能夠讀取相應(yīng)的數(shù)據(jù)。不像通常數(shù)據(jù)庫,查詢后,查詢結(jié)果是從數(shù)據(jù)庫拷貝一份出來放在內(nèi)存中的。而Realm的查詢結(jié)果應(yīng)該說是數(shù)據(jù)庫數(shù)據(jù)的引用,就算你查出來,如果不用也不會占用什么內(nèi)存。

10,支持斷言查詢(Predicate),這樣可以通過條件查詢特定數(shù)據(jù)

同時可以使用鏈式查詢數(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)

//使用鏈式查詢
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ù)合表達式類型(aggregate expression type),但是我們支持對對象的值使用 BETWEEN 操作符類型。比如說,Results<Person>.filter("age BETWEEN %@", [42, 43]])。

11,查詢結(jié)果的排序

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

12,使用List實現(xiàn)一對多關(guān)系

List 中可以包含簡單類型的 Object,表面上和可變的 Array 非常類似。
注意:List 只能夠包含 Object 類型,不能包含諸如String之類的基礎(chǔ)類型。
如果打算給我們的 Person 數(shù)據(jù)模型添加一個“dogs”屬性,以便能夠和多個“dogs”建立關(guān)系,也就是表明一個 Person 可以有多個 Dog,那么我們可以聲明一個List<dog style="outline: 0px;">類型的屬性:</dog>

class Person: Object {
    ... // 其余的屬性聲明`
    let dogs = List<Dog>()
}

// 這里我們就可以使用已存在的狗狗對象來完成初始化

let aPerson = Person(value: ["李四", 30, [aDog, anotherDog]])

// 還可以使用多重嵌套

let aPerson = Person(value: ["李四", 30, [["小黑", 5], ["旺財", 6]]])

可以和之前一樣,對 List 屬性進行訪問和賦值:

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)系,說明其當中包含了 Person 對象。

class Dog: Object {
    @objc dynamic var name = ""
    @objc 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 {
    @objc dynamic var id = 0
    @objc dynamic var name = ""

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

14,添加索引屬性(Indexed Properties)

重寫 Object.indexedProperties() 方法可以為數(shù)據(jù)模型中需要添加索引的屬性建立索引:

class Book: Object {
    @objc dynamic var price = 0

    @objc 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 {
    @objc dynamic var tmpID = 0

    var name: String { // 計算屬性將被自動忽略
        return "\(firstName) \(lastName)"
    }

    @objc dynamic var firstName = ""

    @objc 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:) 來更新對象(當對象不存在時也會自動插入新的對象。)

/****** 方式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)修改默認的的數(shù)據(jù)庫

通過調(diào)用 Realm() 來初始化以及訪問我們的 realm 變量。其指向的是應(yīng)用的 Documents 文件夾下的一個名為“default.realm”的文件。

通過對默認配置進行更改,我們可以使用不同的數(shù)據(jù)庫。比如給每個用戶帳號創(chuàng)建一個特有的 Realm 文件,通過切換配置,就可以直接使用默認的 Realm 數(shù)據(jù)庫來直接訪問各自數(shù)據(jù)庫:

func setDefaultRealmForUser(username: String) {
    var config = Realm.Configuration()
    // 使用默認的目錄,但是使用用戶名來替換默認的文件名
    config.fileURL = config.fileURL!.deletingLastPathComponent().appendingPathComponent("\(username).realm")

    // 將這個配置應(yīng)用到默認的 Realm 數(shù)據(jù)庫當中
    Realm.Configuration.defaultConfiguration = config
}

(2)打包進項目里的數(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 文件不能在沒有密碼保護的 iOS 設(shè)備中進行加密。為了避免這些問題(或者您想構(gòu)建一個 OS X 的應(yīng)用),可以使用 Realm 提供的加密方法。
(3)加密過的 Realm 只會帶來很少的額外資源占用(通常最多只會比平常慢10%)。

/*****   在創(chuàng)建 Realm 數(shù)據(jù)庫時采用64位的密鑰對數(shù)據(jù)庫文件進行 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 {
    @objc dynamic var firstName = ""
    @objc dynamic var lastName = ""
    @objc dynamic var age = 0
}

假如我們想要更新數(shù)據(jù)模型,給它添加一個 fullname 屬性, 而不是將“姓”和“名”分離開來。

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

在這個時候如果您在數(shù)據(jù)模型更新之前就已經(jīng)保存了數(shù)據(jù)的話,那么 Realm 就會注意到代碼和硬盤上數(shù)據(jù)不匹配。 每當這時,您必須進行數(shù)據(jù)遷移,否則當您試圖打開這個文件的話 Realm 就會拋出錯誤。

(2)如何進行數(shù)據(jù)遷移
假設(shè)我們想要把上面所聲明 Person 數(shù)據(jù)模型進行遷移。如下所示是最簡單的數(shù)據(jù)遷移的必需流程:

// 在(application:didFinishLaunchingWithOptions:)中進行配置
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
    // 目前我們還未進行數(shù)據(jù)遷移,因此 oldSchemaVersion == 0
    if (oldSchemaVersion < 1) {
        // 什么都不要做!Realm 會自行檢測新增和需要移除的屬性,然后自動更新硬盤上的數(shù)據(jù)庫架構(gòu)
    }
})

    // 告訴 Realm 為默認的 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 對象進行訪問,而更新之后的實例應(yīng)該通過 newObject 進行訪問:

// 在 application(application:didFinishLaunchingWithOptions:) 中進行配置
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
            // 將名字進行合并,存放在 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 的形式儲存到默認的 Realm 數(shù)據(jù)庫里。

(1)json數(shù)據(jù)格式如下:

{  
    "channels": [
        {
            "name_en": "Personal Radio",
            "seq_id": 0,
            "abbr_en": "My",
            "name": "私人兆赫",
            "channel_id": 0
        },
        {
            "name": "華語",
            "seq_id": 0,
            "abbr_en": "",
            "channel_id": "1",
            "name_en": ""
        },
        {
            "name": "歐美",
            "seq_id": 1,
            "abbr_en": "",
            "channel_id": "2",
            "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)的話,那么就會在插入時被忽略。

import UIKit
import RealmSwift

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // 調(diào)用API
        let url = URL(string: "[http://www.douban.com/j/app/radio/channels](http://www.douban.com/j/app/radio/channels)")!
        let response = try! Data(contentsOf: url)

        // 對 JSON 的回應(yīng)數(shù)據(jù)進行反序列化操作
        let json = try! JSONSerialization.jsonObject(with: response, options: .allowFragments) as! [String: Any]
        let channels = json["channels"] as! [[String: Any]]
        let realm = try! Realm()

        try! realm.write {
            // 為數(shù)組中的每個元素保存一個對象(以及其依賴對象)
            for channel in channels {
                if channel["seq_id"] as! Int == 0 {continue} //第一個頻道數(shù)據(jù)有問題,丟棄掉
                realm.create(DoubanChannel.self, value: channel, update: true)
        }
    }

        print(realm.configuration.fileURL ?? "")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

//豆瓣頻道
class DoubanChannel: Object {
    //頻道id
    @objc dynamic var channel_id = ""
    //頻道名稱
    @objc dynamic var name = ""
    //頻道英文名稱
    @objc dynamic var name_en = ""
    //排序
    @objc dynamic var seq_id = 0
    @objc dynamic var abbr_en = ""
    //設(shè)置主鍵
    override static func primaryKey() -> String? {
        return "channel_id"
    }
}

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

22,當前版本的限制

Realm 致力于平衡數(shù)據(jù)庫讀取的靈活性和性能。為了實現(xiàn)這個目標,在 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)對字符串進行排序以及不區(qū)分大小寫查詢只支持“基礎(chǔ)拉丁字符集”、“拉丁字符補充集”、“拉丁文擴展字符集 A” 以及”拉丁文擴展字符集 B“(UTF-8 的范圍在 0~591 之間)。

原文出自:www.hangge.com 轉(zhuǎn)載請保留原文鏈接:http://www.hangge.com/blog/cache/detail_891.html

最后編輯于
?著作權(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)容