Swift-Realm数据库的使用详解
概述
Realm 是一个跨平台的移动数据库引擎,其性能要优于 Core Data 和 FMDB - 移动端数据库性能比较, 我们可以在 Android 端 realm-java,iOS端:Realm-Cocoa,同时支持 OC 和 Swift两种语言开发。其使用简单,免费,性能优异,跨平台的特点广受程序员GG喜爱。
Realm 中文文档
本文将结合一些实战演练讲解 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() |
Int | dynamic var value = 0 | let value = RealmOptional() |
Float | dynamic var value: Float = 0.0 | let value = RealmOptional() |
Double | dynamic var value: Double = 0.0 | let value = RealmOptional() |
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() | 必须是非可选值 |
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 = 2let docPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] as Stringlet 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 = configRealm.asyncOpen { (realm, error) inif let _ = realm {print("Realm 服务器配置成功!")}else if let error = error {print("Realm 数据库配置失败:\(error.localizedDescription)")}}}
复制代码
定义模型
import UIKit
import RealmSwiftclass 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 ,因为在 swift 运行时无法表示泛型属性。 注意: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 Stringlet 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 = 26stu.id = 2;let birthdayStr = "1993-06-10"let dateFormatter = DateFormatter()dateFormatter.dateFormat = "YYYY-MM-dd"stu.birthday = dateFormatter.date(from: birthdayStr)! as NSDatestu.weight = 160stu.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 = 26stu.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个的学生对象
保存方法
/// 保存一些Studentpublic 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 = 26stu.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 NSDatestus.append(stu)}XWStudentRealmTool.insertStudents(by: stus)}
复制代码
演示结果:
2 查
2.1 普通查询: 查询数据库中所有学生模型并输出姓名,图片,所拥有的书信息
/// 获取 所保存的 Studentpublic 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 (主键) 的 Studentpublic 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 的学生模型并输出姓名
/// 获取 指定条件 的 Studentpublic 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 主键更新 - 更新单个学生
/// 更新单个 Studentpublic class func updateStudent(student : Student) {let defaultRealm = self.getDB()try! defaultRealm.write {defaultRealm.add(student, update: true)}}
复制代码
3.2 主键更新 - 更新多个学生
/// 更新多个 Studentpublic 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 = 27stu.id = i;stus.append(stu)}XWStudentRealmTool.updateStudent(students: stus)}
复制代码
更新之前:
更新之后:
3.3 键值更新 - 所有学生 年龄 改为 18
/// 更新多个 Studentpublic 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 删
/// 删除单个 Studentpublic class func deleteStudent(student : Student) {let defaultRealm = self.getDB()try! defaultRealm.write {defaultRealm.delete(student)}}/// 删除多个 Studentpublic 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