Swift-Realm數據庫的使用詳解
概述
Realm 是一個跨平臺的移動數據庫引擎,其性能要優于 Core Data 和 FMDB - 移動端數據庫性能比較, 我們可以在 Android 端 realm-java,iOS端:Realm-Cocoa,同時支持 OC 和 Swift兩種語言開發。其使用簡單,免費,性能優異,跨平臺的特點廣受程序員GG喜愛。
本文將結合一些實戰演練講解 Realm 的用法,干貨滿滿!
Realm 支持如下屬性的存儲
- Int,Int8,Int16,Int32 和 Int64
- Boolean 、 Bool
- Double 、 Float
- String
- NSDate 、 Date(精度到秒)
- NSData 、 Data
- 繼承自 Object 的類 => 作為一對一關系(Used for One-to-one relations)
- List => 作為一對多關系(Used for one-to-many relations)
如下表是在代碼中聲明的實例:
類型 | 非可選值形式 | 可選值形式 |
---|---|---|
Bool | dynamic var value = false | let value = RealmOptional<Bool>() |
Int | dynamic var value = 0 | let value = RealmOptional<Int>() |
Float | dynamic var value: Float = 0.0 | let value = RealmOptional<Float>() |
Double | dynamic var value: Double = 0.0 | let value = RealmOptional<Double>() |
String | dynamic var value = "" | dynamic var value: String? = nil |
Data | dynamic var value = NSData() | dynamic var value: NSData? = nil |
Date | dynamic var value = NSDate() | dynamic var value: NSDate? = nil |
Object | 必須是可選值 | dynamic var value: Class? |
List | let value = List<Class>() | 必須是非可選值 |
LinkingObjects | let value = LinkingObjects(fromType: Class.self, property: "property") | 必須是非可選值 |
Realm 安裝 - 使用 CocoaPods
pod 'RealmSwift'
pod 'Realm'
Realm 配置
- 將以下代碼寫在 AppDelegate 的 didFinishLaunchingWithOptions 方法中,這個方法主要用于數據模型屬性增加或刪除時的數據遷移,每次模型屬性變化時,將 dbVersion 加 1 即可,Realm 會自行檢測新增和需要移除的屬性,然后自動更新硬盤上的數據庫架構,移除屬性的數據將會被刪除。
/// 配置數據庫
public class func configRealm() {
/// 如果要存儲的數據模型屬性發生變化,需要配置當前版本號比之前大
let dbVersion : UInt64 = 2
let docPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] as String
let dbPath = docPath.appending("/defaultDB.realm")
let config = Realm.Configuration(fileURL: URL.init(string: dbPath), inMemoryIdentifier: nil, syncConfiguration: nil, encryptionKey: nil, readOnly: false, schemaVersion: dbVersion, migrationBlock: { (migration, oldSchemaVersion) in
}, deleteRealmIfMigrationNeeded: false, shouldCompactOnLaunch: nil, objectTypes: nil)
Realm.Configuration.defaultConfiguration = config
Realm.asyncOpen { (realm, error) in
if let _ = realm {
print("Realm 服務器配置成功!")
}else if let error = error {
print("Realm 數據庫配置失敗:\(error.localizedDescription)")
}
}
}
定義模型
import UIKit
import RealmSwift
class Book: Object {
@objc dynamic var name = ""
@objc dynamic var author = ""
/// LinkingObjects 反向表示該對象的擁有者
let owners = LinkingObjects(fromType: Student.self, property: "books")
}
class Student: Object {
@objc dynamic var name = ""
@objc dynamic var age = 18
@objc dynamic var weight = 156
@objc dynamic var id = 0
@objc dynamic var address = ""
@objc dynamic var birthday : NSDate? = nil
@objc dynamic var photo : NSData? = nil
//重寫 Object.primaryKey() 可以設置模型的主鍵。
//聲明主鍵之后,對象將被允許查詢,更新速度更加高效,并且要求每個對象保持唯一性。
//一旦帶有主鍵的對象被添加到 Realm 之后,該對象的主鍵將不可修改。
override static func primaryKey() -> String? {
return "id"
}
//重寫 Object.ignoredProperties() 可以防止 Realm 存儲數據模型的某個屬性
override static func ignoredProperties() -> [String] {
return ["tempID"]
}
//重寫 Object.indexedProperties() 方法可以為數據模型中需要添加索引的屬性建立索引,Realm 支持為字符串、整型、布爾值以及 Date 屬性建立索引。
override static func indexedProperties() -> [String] {
return ["name"]
}
//List 用來表示一對多的關系:一個 Student 中擁有多個 Book。
let books = List<Book>()
}
需要注意的是:在使用Realm中存儲的數據模型都要是 Object
類的子類。
1) 設置主鍵 - primaryKey
重寫 Object.primaryKey() 可以設置模型的主鍵。
聲明主鍵之后,對象將被允許查詢,更新速度更加高效,并且要求每個對象保持唯一性。
一旦帶有主鍵的對象被添加到 Realm 之后,該對象的主鍵將不可修改。
override static func primaryKey() -> String? {
return "id"
}
2) 忽略屬性 - ignoredProperties
重寫 Object.ignoredProperties() 可以防止 Realm 存儲數據模型的某個屬性。Realm 將不會干涉這些屬性的常規操作,它們將由成員變量(var)提供支持,并且您能夠輕易重寫它們的 setter 和 getter。
override static func ignoredProperties() -> [String] {
return ["tempID"]
}
3)索引屬性 - indexedProperties
重寫 Object.indexedProperties() 方法可以為數據模型中需要添加索引的屬性建立索引,Realm 支持為字符串、整型、布爾值以及 Date 屬性建立索引。
override static func indexedProperties() -> [String] {
return ["name"]
}
4)使用List實現一對多關系 - indexedProperties
List 用來表示一對多的關系:一個 Student 中擁有多個 Book。
List 中可以包含簡單類型的 Object,表面上和可變的 Array 非常類似,所用的方法和訪問數據的方式(索引和下標)都相同,并且所包含的所有對象都應該是相同類型的。聲明前面不可加 dynamic ,因為在 Objective-C 運行時無法表示泛型屬性。
注意:List 只能夠包含 Object 類型,不能包含諸如String之類的基礎類型。
//List 用來表示一對多的關系:一個 Student 中擁有多個 Book。
let books = List<Book>()
5)反向關系 - LinkingObjects
通過反向關系(也被稱為反向鏈接(backlink)),您可以通過一個特定的屬性獲取和給定對象有關系的所有對象。 Realm 提供了“鏈接對象 (linking objects)” 屬性來表示這些反向關系。借助鏈接對象屬性,您可以通過指定的屬性來獲取所有鏈接到指定對象的對象。
例如:一個 Book 對象可以擁有一個名為 owners 的鏈接對象屬性,這個屬性中包含了某些 Student 對象,而這些 Student 對象在其 books 屬性中包含了這一個確定的 Book 對象。您可以將 owners 屬性設置為 LinkingObjects 類型,然后指定其關系,說明其當中包含了其擁有者 Student 對象。
let owners = LinkingObjects(fromType: Student.self, property: "books")
1 增
1.1 需求: 插入 1 名學生信息到本地數據庫?
import UIKit
import RealmSwift
class XWStudentRealmTool: Object {
private class func getDB() -> Realm {
let docPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] as String
let dbPath = docPath.appending("/defaultDB.realm")
/// 傳入路徑會自動創建數據庫
let defaultRealm = try! Realm(fileURL: URL.init(string: dbPath)!)
return defaultRealm
}
}
/// 增
extension XWStudentRealmTool {
public class func insertStudent(by student : Student) -> Void {
let defaultRealm = self.getDB()
try! defaultRealm.write {
defaultRealm.add(student)
}
print(defaultRealm.configuration.fileURL ?? "")
}
}
測試代碼
func testInsterStudent() {
let stu = Student()
stu.name = "極客學偉"
stu.age = 26
stu.id = 2;
let birthdayStr = "1993-06-10"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYY-MM-dd"
stu.birthday = dateFormatter.date(from: birthdayStr)! as NSDate
stu.weight = 160
stu.address = "回龍觀"
XWStudentRealmTool.insertStudent(by: stu)
}
通過 Realm Browser 查看剛才保存的數據
通過 print(realm.configuration.fileURL ?? "")
打印數據路徑
效果:
1.2 需求: 測試在數據庫中插入一個擁有多本書并且有頭像的學生對象
測試代碼
//測試在數據庫中插入一個擁有多本書并且有頭像的學生對象
func testInsertStudentWithPhotoBook() {
let stu = Student()
stu.name = "極客學偉_有頭像_有書"
stu.weight = 151;
stu.age = 26
stu.id = 3;
// 頭像
stu.setPhotoWitName("cat")
let bookFubaba = Book.init(name: "富爸爸窮爸爸", author: "[美]羅伯特.T.清崎")
let bookShengmingbuxi = Book.init(name: "生命不息, 折騰不止", author: "羅永浩")
let bookDianfuzhe = Book(value: ["顛覆著: 周鴻祎自傳","周鴻祎"])
stu.books.append(bookFubaba);
stu.books.append(bookShengmingbuxi);
stu.books.append(bookDianfuzhe);
XWStudentRealmTool.insertStudent(by: stu)
}
運行結果:
會自動創建數據庫并新建兩個表 Student 和 Book, 并將兩者進行關聯
1.3 需求: 測試在數據庫中插入44個的學生對象
保存方法
/// 保存一些Student
public class func insertStudents(by students : [Student]) -> Void {
let defaultRealm = self.getDB()
try! defaultRealm.write {
defaultRealm.add(students)
}
print(defaultRealm.configuration.fileURL ?? "")
}
測試代碼
//測試在數據庫中插入多個擁有多本書并且有頭像的學生對象
func testInsertManyStudent() {
var stus = [Student]()
for i in 100...144 {
let stu = Student()
stu.name = "極客學偉_\(i)"
stu.weight = 151;
stu.age = 26
stu.id = i;
// 頭像
stu.setPhotoWitName("cat")
let birthdayStr = "1993-06-10"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYY-MM-dd"
stu.birthday = dateFormatter.date(from: birthdayStr)! as NSDate
stus.append(stu)
}
XWStudentRealmTool.insertStudents(by: stus)
}
演示結果:
2 查
2.1 普通查詢: 查詢數據庫中所有學生模型并輸出姓名,圖片,所擁有的書信息
/// 獲取 所保存的 Student
public class func getStudents() -> Results<Student> {
let defaultRealm = self.getDB()
return defaultRealm.objects(Student.self)
}
測試代碼
let stus = XWStudentRealmTool.getStudents()
for stu in stus {
print(stu.name)
if stu.photo != nil {
self.imageV.image = stu.getPhotoImage()
}
if stu.books.count > 0 {
for book in stu.books {
print(book.name + "+" + book.author)
}
}
}
演示結果:
輸出姓名和書籍信息
將獲取的頭像為imageView賦值
2.2 主鍵查詢: 查詢數據庫中id 為 110 的學生模型并輸出姓名
/// 獲取 指定id (主鍵) 的 Student
public class func getStudent(from id : Int) -> Student? {
let defaultRealm = self.getDB()
return defaultRealm.object(ofType: Student.self, forPrimaryKey: id)
}
測試代碼
// 通過主鍵查詢
func testSearchStudentByID(){
let student = XWStudentRealmTool.getStudent(from: 110)
if let studentL = student {
print(studentL.name)
}
}
演示結果:
對應數據庫中:
2.2 主鍵查詢: 查詢數據庫中id 為 110 的學生模型并輸出姓名
/// 獲取 指定條件 的 Student
public class func getStudentByTerm(_ term: String) -> Results<Student> {
let defaultRealm = self.getDB()
print(defaultRealm.configuration.fileURL ?? "")
let predicate = NSPredicate(format: term)
let results = defaultRealm.objects(Student.self)
return results.filter(predicate)
}
測試代碼
// 條件查詢
func testSearchTermStudent() {
let students = XWStudentRealmTool.getStudentByTerm("name = '極客學偉_110'")
if students.count == 0 {
print("未查詢到任何數據")
return
}
for student in students {
print(student.name,student.weight)
}
}
輸出結果:
升序/降序 查詢
// 根據名字升序查詢
let stus = realm.objects(Student.self).sorted(byKeyPath: "id")
// 根據名字降序序查詢
let stus = realm.objects(Student.self).sorted(byKeyPath: "id", ascending: false)
3 改
3.1 主鍵更新 - 更新單個學生
/// 更新單個 Student
public class func updateStudent(student : Student) {
let defaultRealm = self.getDB()
try! defaultRealm.write {
defaultRealm.add(student, update: true)
}
}
3.2 主鍵更新 - 更新多個學生
/// 更新多個 Student
public class func updateStudent(students : [Student]) {
let defaultRealm = self.getDB()
try! defaultRealm.write {
defaultRealm.add(students, update: true)
}
}
測試代碼
/// 批量更改
func testUpdateStudents() {
var stus = [Student]()
for i in 100...144 {
let stu = Student()
stu.name = "極客學偉改名_\(i)"
stu.weight = 148;
stu.age = 27
stu.id = i;
stus.append(stu)
}
XWStudentRealmTool.updateStudent(students: stus)
}
更新之前:
更新之后:
3.3 鍵值更新 - 所有學生 年齡 改為 18
/// 更新多個 Student
public class func updateStudentAge(age : Int) {
let defaultRealm = self.getDB()
try! defaultRealm.write {
let students = defaultRealm.objects(Student.self)
students.setValue(age, forKey: "age")
}
}
測試代碼:
/// 批量更改年齡
func testUpdateStudentsAge() {
XWStudentRealmTool.updateStudentAge(age: 18)
}
演示結果:
4 刪
/// 刪除單個 Student
public class func deleteStudent(student : Student) {
let defaultRealm = self.getDB()
try! defaultRealm.write {
defaultRealm.delete(student)
}
}
/// 刪除多個 Student
public class func deleteStudent(students : Results<Student>) {
let defaultRealm = self.getDB()
try! defaultRealm.write {
defaultRealm.delete(students)
}
}
測試代碼:
/// 刪除id 為 3 的學生
func testDeleteOneStudent() {
let stu = XWStudentRealmTool.getStudent(from: 3)
if stu != nil {
XWStudentRealmTool.deleteStudent(student: stu!)
}
}
/// 刪除所有
func testDeleteAllStudent() {
let stus = XWStudentRealmTool.getStudents()
XWStudentRealmTool.deleteStudent(students: stus)
}
刪除后的數據庫:
空空如也
文中所有演示代碼 -> XWRealmSwiftDemo In Github